aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mfd/rk808.txt188
-rw-r--r--Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/cs42l42.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-card.yaml122
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audmux.txt28
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audmux.yaml119
-rw-r--r--Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml58
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml30
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/wm8750.txt18
-rw-r--r--Documentation/devicetree/bindings/sound/wm8750.yaml42
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/mfd/rk808.c81
-rw-r--r--include/linux/mfd/rk808.h81
-rw-r--r--include/linux/mfd/wcd934x/registers.h57
-rw-r--r--include/sound/soc-dai.h57
-rw-r--r--include/sound/soc-topology.h2
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c10
-rw-r--r--sound/soc/amd/raven/acp3x.h1
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c15
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c4
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c3
-rw-r--r--sound/soc/codecs/Kconfig25
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/ak4613.c11
-rw-r--r--sound/soc/codecs/ak5558.c2
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cs35l32.c37
-rw-r--r--sound/soc/codecs/cs35l33.c16
-rw-r--r--sound/soc/codecs/cs35l34.c42
-rw-r--r--sound/soc/codecs/cs35l35.c37
-rw-r--r--sound/soc/codecs/cs35l35.h3
-rw-r--r--sound/soc/codecs/cs35l36.c18
-rw-r--r--sound/soc/codecs/cs4265.c10
-rw-r--r--sound/soc/codecs/cs42l42.c201
-rw-r--r--sound/soc/codecs/cs42l42.h5
-rw-r--r--sound/soc/codecs/cs42l52.c19
-rw-r--r--sound/soc/codecs/cs42l56.c14
-rw-r--r--sound/soc/codecs/cs42l73.c33
-rw-r--r--sound/soc/codecs/cs43130.c55
-rw-r--r--sound/soc/codecs/cs47l24.c2
-rw-r--r--sound/soc/codecs/cs53l30.c25
-rw-r--r--sound/soc/codecs/da7219.c5
-rw-r--r--sound/soc/codecs/hdmi-codec.c21
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c3
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c1
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c4
-rw-r--r--sound/soc/codecs/max98088.c13
-rw-r--r--sound/soc/codecs/mt6359-accdet.c2
-rw-r--r--sound/soc/codecs/mt6359.c2
-rw-r--r--sound/soc/codecs/pcm3168a.c26
-rw-r--r--sound/soc/codecs/rk3328_codec.c28
-rw-r--r--sound/soc/codecs/rk817_codec.c541
-rw-r--r--sound/soc/codecs/rt1019.c5
-rw-r--r--sound/soc/codecs/rt286.c1
-rw-r--r--sound/soc/codecs/rt5682-i2c.c12
-rw-r--r--sound/soc/codecs/rt711-sdca.c4
-rw-r--r--sound/soc/codecs/sigmadsp.h1
-rw-r--r--sound/soc/codecs/sti-sas.c1
-rw-r--r--sound/soc/codecs/tfa989x.c357
-rw-r--r--sound/soc/codecs/tlv320aic26.c12
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1475
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h340
-rw-r--r--sound/soc/codecs/wcd934x.c884
-rw-r--r--sound/soc/codecs/wm2200.c1
-rw-r--r--sound/soc/codecs/wm5102.c2
-rw-r--r--sound/soc/codecs/wm5110.c2
-rw-r--r--sound/soc/codecs/wm8962.c7
-rw-r--r--sound/soc/codecs/wm_adsp.c18
-rw-r--r--sound/soc/codecs/wm_adsp.h2
-rw-r--r--sound/soc/fsl/Kconfig14
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_spdif.c67
-rw-r--r--sound/soc/fsl/fsl_spdif.h1
-rw-r--r--sound/soc/fsl/fsl_xcvr.c2
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c12
-rw-r--r--sound/soc/fsl/imx-card.c844
-rw-r--r--sound/soc/fsl/imx-es8328.c12
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c6
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.h4
-rw-r--r--sound/soc/fsl/imx-rpmsg.c1
-rw-r--r--sound/soc/generic/audio-graph-card.c57
-rw-r--r--sound/soc/generic/simple-card.c168
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c14
-rw-r--r--sound/soc/img/img-i2s-in.c2
-rw-r--r--sound/soc/intel/boards/Kconfig36
-rw-r--r--sound/soc/intel/boards/Makefile30
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c13
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c1
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c1
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c1
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c1
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c1
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c26
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c1
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c1
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c1
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c1
-rw-r--r--sound/soc/intel/boards/cml_rt1011_rt5682.c1
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c2
-rw-r--r--sound/soc/intel/boards/glk_rt5682_max98357a.c1
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c5
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c1
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c509
-rw-r--r--sound/soc/intel/boards/sof_da7219_max98373.c2
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c84
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h14
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c2
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c120
-rw-r--r--sound/soc/intel/boards/sof_sdw.c71
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h8
-rw-r--r--sound/soc/intel/boards/sof_sdw_max98373.c4
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c1
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c71
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c12
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c6
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c4
-rw-r--r--sound/soc/jz4740/jz4740-i2s.h2
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c24
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c2
-rw-r--r--sound/soc/meson/g12a-toacodec.c143
-rw-r--r--sound/soc/qcom/lpass-cpu.c12
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c2
-rw-r--r--sound/soc/qcom/sdm845.c8
-rw-r--r--sound/soc/sh/fsi.c15
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/adg.c180
-rw-r--r--sound/soc/sh/rcar/cmd.c14
-rw-r--r--sound/soc/sh/rcar/core.c154
-rw-r--r--sound/soc/sh/rcar/ctu.c14
-rw-r--r--sound/soc/sh/rcar/debugfs.c96
-rw-r--r--sound/soc/sh/rcar/dma.c39
-rw-r--r--sound/soc/sh/rcar/dvc.c16
-rw-r--r--sound/soc/sh/rcar/gen.c9
-rw-r--r--sound/soc/sh/rcar/mix.c14
-rw-r--r--sound/soc/sh/rcar/rsnd.h75
-rw-r--r--sound/soc/sh/rcar/src.c32
-rw-r--r--sound/soc/sh/rcar/ssi.c160
-rw-r--r--sound/soc/sh/rcar/ssiu.c147
-rw-r--r--sound/soc/soc-core.c298
-rw-r--r--sound/soc/soc-dai.c76
-rw-r--r--sound/soc/soc-topology.c466
-rw-r--r--sound/soc/soc-utils.c29
-rw-r--r--sound/soc/sof/compress.c2
-rw-r--r--sound/soc/sof/compress.h2
-rw-r--r--sound/soc/sof/intel/Makefile5
-rw-r--r--sound/soc/sof/intel/atom.c463
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/byt.c768
-rw-r--r--sound/soc/sof/intel/hda-dai.c5
-rw-r--r--sound/soc/sof/intel/hda-loader.c5
-rw-r--r--sound/soc/sof/intel/hda.c27
-rw-r--r--sound/soc/sof/intel/pci-tgl.c2
-rw-r--r--sound/soc/sof/intel/pci-tng.c171
-rw-r--r--sound/soc/sof/loader.c2
-rw-r--r--sound/soc/sof/ops.h10
-rw-r--r--sound/soc/sof/sof-acpi-dev.c5
-rw-r--r--sound/soc/sof/sof-of-dev.c5
-rw-r--r--sound/soc/sof/sof-pci-dev.c7
-rw-r--r--sound/soc/sof/topology.c17
-rw-r--r--sound/soc/stm/stm32_sai_sub.c5
-rw-r--r--sound/soc/ti/davinci-mcasp.c1
-rw-r--r--sound/soc/ti/omap-mcbsp.c2
165 files changed, 9215 insertions, 1934 deletions
diff --git a/Documentation/devicetree/bindings/mfd/rk808.txt b/Documentation/devicetree/bindings/mfd/rk808.txt
index 04df07f6f793..23a17a6663ec 100644
--- a/Documentation/devicetree/bindings/mfd/rk808.txt
+++ b/Documentation/devicetree/bindings/mfd/rk808.txt
@@ -23,6 +23,7 @@ Optional properties:
default output clock name
- rockchip,system-power-controller: Telling whether or not this pmic is controlling
the system power.
+- wakeup-source: Device can be used as a wakeup source.
Optional RK805 properties:
- vcc1-supply: The input supply for DCDC_REG1
@@ -63,8 +64,18 @@ Optional RK809 properties:
- vcc9-supply: The input supply for DCDC_REG5, SWITCH_REG2
Optional RK817 properties:
+- clocks: The input clock for the audio codec
+- clock-names: The clock name for the codec clock. Should be "mclk".
+- #sound-dai-cells: Needed for the interpretation of sound dais. Should be 0.
+
- vcc8-supply: The input supply for BOOST
- vcc9-supply: The input supply for OTG_SWITCH
+- codec: The child node for the codec to hold additional properties.
+ If no additional properties are required for the codec, this
+ node can be omitted.
+
+- rockchip,mic-in-differential: Telling if the microphone uses differential
+ mode. Should be under the codec child node.
Optional RK818 properties:
- vcc1-supply: The input supply for DCDC_REG1
@@ -275,3 +286,180 @@ Example:
};
};
};
+
+ rk817: pmic@20 {
+ compatible = "rockchip,rk817";
+ reg = <0x20>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PB2 IRQ_TYPE_LEVEL_LOW>;
+ clock-output-names = "rk808-clkout1", "xin32k";
+ clock-names = "mclk";
+ clocks = <&cru SCLK_I2S1_OUT>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_int>, <&i2s1_2ch_mclk>;
+ wakeup-source;
+ #clock-cells = <1>;
+ #sound-dai-cells = <0>;
+
+ vcc1-supply = <&vccsys>;
+ vcc2-supply = <&vccsys>;
+ vcc3-supply = <&vccsys>;
+ vcc4-supply = <&vccsys>;
+ vcc5-supply = <&vccsys>;
+ vcc6-supply = <&vccsys>;
+ vcc7-supply = <&vccsys>;
+
+ regulators {
+ vdd_logic: DCDC_REG1 {
+ regulator-name = "vdd_logic";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1150000>;
+ regulator-ramp-delay = <6001>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <950000>;
+ };
+ };
+
+ vdd_arm: DCDC_REG2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-ramp-delay = <6001>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <950000>;
+ };
+ };
+
+ vcc_ddr: DCDC_REG3 {
+ regulator-name = "vcc_ddr";
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vcc_3v3: DCDC_REG4 {
+ regulator-name = "vcc_3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vcc_1v8: LDO_REG2 {
+ regulator-name = "vcc_1v8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd_1v0: LDO_REG3 {
+ regulator-name = "vdd_1v0";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1000000>;
+ };
+ };
+
+ vcc3v3_pmu: LDO_REG4 {
+ regulator-name = "vcc3v3_pmu";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vccio_sd: LDO_REG5 {
+ regulator-name = "vccio_sd";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vcc_sd: LDO_REG6 {
+ regulator-name = "vcc_sd";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vcc_bl: LDO_REG7 {
+ regulator-name = "vcc_bl";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vcc_lcd: LDO_REG8 {
+ regulator-name = "vcc_lcd";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <2800000>;
+ };
+ };
+
+ vcc_cam: LDO_REG9 {
+ regulator-name = "vcc_cam";
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <3000000>;
+ };
+ };
+ };
+
+ rk817_codec: codec {
+ rockchip,mic-in-differential;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
index 67405e6d8168..19f111f40225 100644
--- a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
@@ -12,7 +12,11 @@ maintainers:
properties:
"#sound-dai-cells":
- const: 0
+ minimum: 0
+ maximum: 1
+ description:
+ A value of 0 is deprecated. When used, it only allows access to
+ the ADC/DAC and AIF1 (the CPU DAI), not the other two AIFs/DAIs.
compatible:
oneOf:
@@ -50,7 +54,7 @@ additionalProperties: false
examples:
- |
audio-codec@1c22e00 {
- #sound-dai-cells = <0>;
+ #sound-dai-cells = <1>;
compatible = "allwinner,sun8i-a33-codec";
reg = <0x01c22e00 0x400>;
interrupts = <0 29 4>;
diff --git a/Documentation/devicetree/bindings/sound/cs42l42.txt b/Documentation/devicetree/bindings/sound/cs42l42.txt
index 7dfaa2ab906f..5d416fdaf023 100644
--- a/Documentation/devicetree/bindings/sound/cs42l42.txt
+++ b/Documentation/devicetree/bindings/sound/cs42l42.txt
@@ -81,6 +81,13 @@ Optional properties:
< x1 x2 x3 x4 >
Default = < 15 8 4 1>
+ - cirrus,hs-bias-sense-disable: This is boolean property. If present the
+ HSBIAS sense is disabled. Configures HSBIAS output current sense through
+ the external 2.21-k resistor. HSBIAS_SENSE is hardware feature to reduce
+ the potential pop noise during the headset plug out slowly. But on some
+ platforms ESD voltage will affect it causing test to fail, especially
+ with CTIA headset type. For different hardware setups, a designer might
+ want to tweak default behavior.
Example:
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
new file mode 100644
index 000000000000..d1816dd061cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/imx-audio-card.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX audio sound card.
+
+maintainers:
+ - Shengjiu Wang <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx-audio-card
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User specified audio sound card name
+
+ audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ A list of the connections between audio components. Each entry is a
+ pair of strings, the first being the connection's sink, the second
+ being the connection's source. Valid names could be power supplies,
+ MicBias of codec and the jacks on the board.
+
+patternProperties:
+ ".*-dai-link$":
+ description:
+ Each subnode represents a dai link. Subnodes of each dai links would be
+ cpu/codec dais.
+
+ type: object
+
+ properties:
+ link-name:
+ description: Indicates dai-link name and PCM stream name.
+ $ref: /schemas/types.yaml#/definitions/string
+ maxItems: 1
+
+ format:
+ description: audio format.
+ items:
+ enum:
+ - i2s
+ - dsp_b
+
+ dai-tdm-slot-num:
+ description: see tdm-slot.txt.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ dai-tdm-slot-width:
+ description: see tdm-slot.txt.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ cpu:
+ description: Holds subnode which indicates cpu dai.
+ type: object
+ properties:
+ sound-dai: true
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ properties:
+ sound-dai: true
+
+ fsl,mclk-equal-bclk:
+ description: Indicates mclk can be equal to bclk, especially for sai interface
+ $ref: /schemas/types.yaml#/definitions/flag
+
+ required:
+ - link-name
+ - cpu
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - model
+
+additionalProperties: false
+
+examples:
+ - |
+ sound-ak4458 {
+ compatible = "fsl,imx-audio-card";
+ model = "ak4458-audio";
+ pri-dai-link {
+ link-name = "akcodec";
+ format = "i2s";
+ fsl,mclk-equal-bclk;
+ cpu {
+ sound-dai = <&sai1>;
+ };
+ codec {
+ sound-dai = <&ak4458_1>, <&ak4458_2>;
+ };
+ };
+ fe-dai-link {
+ link-name = "HiFi-ASRC-FE";
+ format = "i2s";
+ cpu {
+ sound-dai = <&easrc>;
+ };
+ };
+ be-dai-link {
+ link-name = "HiFi-ASRC-BE";
+ format = "dsp_b";
+ dai-tdm-slot-num = <8>;
+ dai-tdm-slot-width = <32>;
+ fsl,mclk-equal-bclk;
+ cpu {
+ sound-dai = <&sai1>;
+ };
+ codec {
+ sound-dai = <&ak4458_1>, <&ak4458_2>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt
deleted file mode 100644
index 2db4dcbee1b9..000000000000
--- a/Documentation/devicetree/bindings/sound/imx-audmux.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Freescale Digital Audio Mux (AUDMUX) device
-
-Required properties:
-
- - compatible : "fsl,imx21-audmux" for AUDMUX version firstly used
- on i.MX21, or "fsl,imx31-audmux" for the version
- firstly used on i.MX31.
-
- - reg : Should contain AUDMUX registers location and length.
-
-An initial configuration can be setup using child nodes.
-
-Required properties of optional child nodes:
-
- - fsl,audmux-port : Integer of the audmux port that is configured by this
- child node.
-
- - fsl,port-config : List of configuration options for the specific port.
- For imx31-audmux and above, it is a list of tuples
- <ptcr pdcr>. For imx21-audmux it is a list of pcr
- values.
-
-Example:
-
-audmux@21d8000 {
- compatible = "fsl,imx6q-audmux", "fsl,imx31-audmux";
- reg = <0x021d8000 0x4000>;
-};
diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.yaml b/Documentation/devicetree/bindings/sound/imx-audmux.yaml
new file mode 100644
index 000000000000..dab45c310670
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audmux.yaml
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/imx-audmux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Digital Audio Mux device
+
+maintainers:
+ - Oleksij Rempel <[email protected]>
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - fsl,imx27-audmux
+ - const: fsl,imx21-audmux
+ - items:
+ - enum:
+ - fsl,imx25-audmux
+ - fsl,imx35-audmux
+ - fsl,imx50-audmux
+ - fsl,imx51-audmux
+ - fsl,imx53-audmux
+ - fsl,imx6q-audmux
+ - fsl,imx6sl-audmux
+ - fsl,imx6sll-audmux
+ - fsl,imx6sx-audmux
+ - const: fsl,imx31-audmux
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: audmux
+
+patternProperties:
+ "^mux-[0-9a-z]*$":
+ type: object
+ properties:
+ fsl,audmux-port:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Integer of the audmux port that is configured by this child node
+
+ fsl,port-config:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description: |
+ List of configuration options for the specific port.
+ For imx31-audmux and above, it is a list of tuples ptcr pdcr.
+ For imx21-audmux it is a list of pcr values.
+
+ required:
+ - fsl,audmux-port
+ - fsl,port-config
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ audmux@21d8000 {
+ compatible = "fsl,imx6q-audmux", "fsl,imx31-audmux";
+ reg = <0x021d8000 0x4000>;
+ };
+ - |
+ audmux@10016000 {
+ compatible = "fsl,imx27-audmux", "fsl,imx21-audmux";
+ reg = <0x10016000 0x1000>;
+ clocks = <&clks 1>;
+ clock-names = "audmux";
+
+ mux-ssi0 {
+ fsl,audmux-port = <0>;
+ fsl,port-config = <0xcb205000>;
+ };
+
+ mux-pins4 {
+ fsl,audmux-port = <2>;
+ fsl,port-config = <0x00001000>;
+ };
+ };
+ - |
+ #include <dt-bindings/sound/fsl-imx-audmux.h>
+ audmux@21d8000 {
+ compatible = "fsl,imx6q-audmux", "fsl,imx31-audmux";
+ reg = <0x021d8000 0x4000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_audmux>;
+
+ mux-ssi1 {
+ fsl,audmux-port = <0>;
+ fsl,port-config = <
+ IMX_AUDMUX_V2_PTCR_SYN 0
+ IMX_AUDMUX_V2_PTCR_TFSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TCSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TFSDIR 0
+ IMX_AUDMUX_V2_PTCR_TCLKDIR IMX_AUDMUX_V2_PDCR_RXDSEL(2)
+ >;
+ };
+
+ mux-pins3 {
+ fsl,audmux-port = <2>;
+ fsl,port-config = <
+ IMX_AUDMUX_V2_PTCR_SYN IMX_AUDMUX_V2_PDCR_RXDSEL(0)
+ 0 IMX_AUDMUX_V2_PDCR_TXRXEN
+ >;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
new file mode 100644
index 000000000000..ffb8fcfeb629
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nxp,tfa989x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP/Goodix TFA989X (TFA1) Audio Amplifiers
+
+maintainers:
+ - Stephan Gerhold <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - nxp,tfa9895
+ - nxp,tfa9897
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ sound-name-prefix:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Used as prefix for sink/source names of the component. Must be a
+ unique string among multiple instances of the same component.
+
+ vddd-supply:
+ description: regulator phandle for the VDDD power supply.
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@34 {
+ compatible = "nxp,tfa9895";
+ reg = <0x34>;
+ sound-name-prefix = "Speaker Left";
+ #sound-dai-cells = <0>;
+ };
+ audio-codec@36 {
+ compatible = "nxp,tfa9895";
+ reg = <0x36>;
+ sound-name-prefix = "Speaker Right";
+ #sound-dai-cells = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
index e8f716b5f875..9b225dbf8b79 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
@@ -77,6 +77,31 @@ properties:
minimum: 1800000
maximum: 2850000
+ qcom,hphl-jack-type-normally-closed:
+ description: Indicates that HPHL jack switch type is normally closed
+ type: boolean
+
+ qcom,ground-jack-type-normally-closed:
+ description: Indicates that Headset Ground switch type is normally closed
+ type: boolean
+
+ qcom,mbhc-headset-vthreshold-microvolt:
+ description: Voltage threshold value for headset detection
+ minimum: 0
+ maximum: 2850000
+
+ qcom,mbhc-headphone-vthreshold-microvolt:
+ description: Voltage threshold value for headphone detection
+ minimum: 0
+ maximum: 2850000
+
+ qcom,mbhc-buttons-vthreshold-microvolt:
+ description:
+ Array of 8 Voltage threshold values corresponding to headset
+ button0 - button7
+ minItems: 8
+ maxItems: 8
+
clock-output-names:
const: mclk
@@ -159,6 +184,11 @@ examples:
qcom,micbias2-microvolt = <1800000>;
qcom,micbias3-microvolt = <1800000>;
qcom,micbias4-microvolt = <1800000>;
+ qcom,hphl-jack-type-normally-closed;
+ qcom,ground-jack-type-normally-closed;
+ qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>;
+ qcom,mbhc-headset-vthreshold-microvolt = <1700000>;
+ qcom,mbhc-headphone-vthreshold-microvolt = <50000>;
clock-names = "extclk";
clocks = <&rpmhcc 2>;
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
index 605de3a5847f..ee936d1aa724 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
@@ -86,9 +86,11 @@ properties:
power-domains: true
resets:
+ minItems: 1
maxItems: 11
reset-names:
+ minItems: 1
maxItems: 11
clocks:
@@ -110,6 +112,13 @@ properties:
- pattern: '^dvc\.[0-1]$'
- pattern: '^clk_(a|b|c|i)$'
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port(@[0-9a-f]+)?:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
port:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
@@ -257,7 +266,6 @@ required:
- "#sound-dai-cells"
allOf:
- - $ref: audio-graph.yaml#
- if:
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/sound/wm8750.txt b/Documentation/devicetree/bindings/sound/wm8750.txt
deleted file mode 100644
index 682f221f6f38..000000000000
--- a/Documentation/devicetree/bindings/sound/wm8750.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-WM8750 and WM8987 audio CODECs
-
-These devices support both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
- - compatible : "wlf,wm8750" or "wlf,wm8987"
-
- - reg : the I2C address of the device for I2C, the chip select
- number for SPI.
-
-Example:
-
-wm8750: codec@1a {
- compatible = "wlf,wm8750";
- reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/wm8750.yaml b/Documentation/devicetree/bindings/sound/wm8750.yaml
new file mode 100644
index 000000000000..24246ac7bbdf
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wm8750.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/wm8750.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: WM8750 and WM8987 audio CODECs
+
+description: |
+ These devices support both I2C and SPI (configured with pin strapping
+ on the board).
+
+maintainers:
+ - Mark Brown <[email protected]>
+
+properties:
+ compatible:
+ enum:
+ - wlf,wm8750
+ - wlf,wm8987
+
+ reg:
+ description:
+ The I2C address of the device for I2C, the chip select number for SPI
+ maxItems: 1
+
+additionalProperties: false
+
+required:
+ - reg
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@1a {
+ compatible = "wlf,wm8750";
+ reg = <0x1a>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index bd7aff0c120f..33079fddc7b8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13204,6 +13204,13 @@ S: Maintained
F: Documentation/devicetree/bindings/sound/tfa9879.txt
F: sound/soc/codecs/tfa9879*
+NXP/Goodix TFA989X (TFA1) DRIVER
+M: Stephan Gerhold <[email protected]>
+L: [email protected] (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml
+F: sound/soc/codecs/tfa989x.c
+
NXP-NCI NFC DRIVER
M: Clément Perrochaud <[email protected]>
R: Charles Gorand <[email protected]>
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index ad923dd4e007..77ccd31ca1d9 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -65,6 +65,7 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
switch (reg) {
case RK817_SECONDS_REG ... RK817_WEEKS_REG:
case RK817_RTC_STATUS_REG:
+ case RK817_CODEC_DTOP_LPT_SRST:
case RK817_INT_STS_REG0:
case RK817_INT_STS_REG1:
case RK817_INT_STS_REG2:
@@ -163,6 +164,7 @@ static const struct mfd_cell rk817s[] = {
.num_resources = ARRAY_SIZE(rk817_rtc_resources),
.resources = &rk817_rtc_resources[0],
},
+ { .name = "rk817-codec",},
};
static const struct mfd_cell rk818s[] = {
@@ -201,6 +203,85 @@ static const struct rk808_reg_data rk808_pre_init_reg[] = {
static const struct rk808_reg_data rk817_pre_init_reg[] = {
{RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
+ /* Codec specific registers */
+ { RK817_CODEC_DTOP_VUCTL, MASK_ALL, 0x03 },
+ { RK817_CODEC_DTOP_VUCTIME, MASK_ALL, 0x00 },
+ { RK817_CODEC_DTOP_LPT_SRST, MASK_ALL, 0x00 },
+ { RK817_CODEC_DTOP_DIGEN_CLKE, MASK_ALL, 0x00 },
+ /* from vendor driver, CODEC_AREF_RTCFG0 not defined in data sheet */
+ { RK817_CODEC_AREF_RTCFG0, MASK_ALL, 0x00 },
+ { RK817_CODEC_AREF_RTCFG1, MASK_ALL, 0x06 },
+ { RK817_CODEC_AADC_CFG0, MASK_ALL, 0xc8 },
+ /* from vendor driver, CODEC_AADC_CFG1 not defined in data sheet */
+ { RK817_CODEC_AADC_CFG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_VOLL, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_VOLR, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_SR_ACL0, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_ALC1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_ALC2, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_NG, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_HPF, MASK_ALL, 0x00 },
+ { RK817_CODEC_DADC_RVOLL, MASK_ALL, 0xff },
+ { RK817_CODEC_DADC_RVOLR, MASK_ALL, 0xff },
+ { RK817_CODEC_AMIC_CFG0, MASK_ALL, 0x70 },
+ { RK817_CODEC_AMIC_CFG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_PGA_GAIN, MASK_ALL, 0x66 },
+ { RK817_CODEC_DMIC_LMT1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_LMT2, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_NG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_NG2, MASK_ALL, 0x00 },
+ /* from vendor driver, CODEC_ADAC_CFG0 not defined in data sheet */
+ { RK817_CODEC_ADAC_CFG0, MASK_ALL, 0x00 },
+ { RK817_CODEC_ADAC_CFG1, MASK_ALL, 0x07 },
+ { RK817_CODEC_DDAC_POPD_DACST, MASK_ALL, 0x82 },
+ { RK817_CODEC_DDAC_VOLL, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_VOLR, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_SR_LMT0, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_LMT1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_LMT2, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_MUTE_MIXCTL, MASK_ALL, 0xa0 },
+ { RK817_CODEC_DDAC_RVOLL, MASK_ALL, 0xff },
+ { RK817_CODEC_DADC_RVOLR, MASK_ALL, 0xff },
+ { RK817_CODEC_AMIC_CFG0, MASK_ALL, 0x70 },
+ { RK817_CODEC_AMIC_CFG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_PGA_GAIN, MASK_ALL, 0x66 },
+ { RK817_CODEC_DMIC_LMT1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_LMT2, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_NG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DMIC_NG2, MASK_ALL, 0x00 },
+ /* from vendor driver, CODEC_ADAC_CFG0 not defined in data sheet */
+ { RK817_CODEC_ADAC_CFG0, MASK_ALL, 0x00 },
+ { RK817_CODEC_ADAC_CFG1, MASK_ALL, 0x07 },
+ { RK817_CODEC_DDAC_POPD_DACST, MASK_ALL, 0x82 },
+ { RK817_CODEC_DDAC_VOLL, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_VOLR, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_SR_LMT0, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_LMT1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_LMT2, MASK_ALL, 0x00 },
+ { RK817_CODEC_DDAC_MUTE_MIXCTL, MASK_ALL, 0xa0 },
+ { RK817_CODEC_DDAC_RVOLL, MASK_ALL, 0xff },
+ { RK817_CODEC_DDAC_RVOLR, MASK_ALL, 0xff },
+ { RK817_CODEC_AHP_ANTI0, MASK_ALL, 0x00 },
+ { RK817_CODEC_AHP_ANTI1, MASK_ALL, 0x00 },
+ { RK817_CODEC_AHP_CFG0, MASK_ALL, 0xe0 },
+ { RK817_CODEC_AHP_CFG1, MASK_ALL, 0x1f },
+ { RK817_CODEC_AHP_CP, MASK_ALL, 0x09 },
+ { RK817_CODEC_ACLASSD_CFG1, MASK_ALL, 0x69 },
+ { RK817_CODEC_ACLASSD_CFG2, MASK_ALL, 0x44 },
+ { RK817_CODEC_APLL_CFG0, MASK_ALL, 0x04 },
+ { RK817_CODEC_APLL_CFG1, MASK_ALL, 0x00 },
+ { RK817_CODEC_APLL_CFG2, MASK_ALL, 0x30 },
+ { RK817_CODEC_APLL_CFG3, MASK_ALL, 0x19 },
+ { RK817_CODEC_APLL_CFG4, MASK_ALL, 0x65 },
+ { RK817_CODEC_APLL_CFG5, MASK_ALL, 0x01 },
+ { RK817_CODEC_DI2S_CKM, MASK_ALL, 0x01 },
+ { RK817_CODEC_DI2S_RSD, MASK_ALL, 0x00 },
+ { RK817_CODEC_DI2S_RXCR1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DI2S_RXCR2, MASK_ALL, 0x17 },
+ { RK817_CODEC_DI2S_RXCMD_TSD, MASK_ALL, 0x00 },
+ { RK817_CODEC_DI2S_TXCR1, MASK_ALL, 0x00 },
+ { RK817_CODEC_DI2S_TXCR2, MASK_ALL, 0x17 },
+ { RK817_CODEC_DI2S_TXCR3_TXCMD, MASK_ALL, 0x00 },
{RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_L},
{RK817_SYS_CFG(1), RK817_HOTDIE_TEMP_MSK | RK817_TSD_TEMP_MSK,
RK817_HOTDIE_105 | RK817_TSD_140},
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index e07f6e61cd38..a96e6d43ca06 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -437,6 +437,87 @@ enum rk809_reg_id {
#define RK817_RTC_COMP_LSB_REG 0x10
#define RK817_RTC_COMP_MSB_REG 0x11
+/* RK817 Codec Registers */
+#define RK817_CODEC_DTOP_VUCTL 0x12
+#define RK817_CODEC_DTOP_VUCTIME 0x13
+#define RK817_CODEC_DTOP_LPT_SRST 0x14
+#define RK817_CODEC_DTOP_DIGEN_CLKE 0x15
+#define RK817_CODEC_AREF_RTCFG0 0x16
+#define RK817_CODEC_AREF_RTCFG1 0x17
+#define RK817_CODEC_AADC_CFG0 0x18
+#define RK817_CODEC_AADC_CFG1 0x19
+#define RK817_CODEC_DADC_VOLL 0x1a
+#define RK817_CODEC_DADC_VOLR 0x1b
+#define RK817_CODEC_DADC_SR_ACL0 0x1e
+#define RK817_CODEC_DADC_ALC1 0x1f
+#define RK817_CODEC_DADC_ALC2 0x20
+#define RK817_CODEC_DADC_NG 0x21
+#define RK817_CODEC_DADC_HPF 0x22
+#define RK817_CODEC_DADC_RVOLL 0x23
+#define RK817_CODEC_DADC_RVOLR 0x24
+#define RK817_CODEC_AMIC_CFG0 0x27
+#define RK817_CODEC_AMIC_CFG1 0x28
+#define RK817_CODEC_DMIC_PGA_GAIN 0x29
+#define RK817_CODEC_DMIC_LMT1 0x2a
+#define RK817_CODEC_DMIC_LMT2 0x2b
+#define RK817_CODEC_DMIC_NG1 0x2c
+#define RK817_CODEC_DMIC_NG2 0x2d
+#define RK817_CODEC_ADAC_CFG0 0x2e
+#define RK817_CODEC_ADAC_CFG1 0x2f
+#define RK817_CODEC_DDAC_POPD_DACST 0x30
+#define RK817_CODEC_DDAC_VOLL 0x31
+#define RK817_CODEC_DDAC_VOLR 0x32
+#define RK817_CODEC_DDAC_SR_LMT0 0x35
+#define RK817_CODEC_DDAC_LMT1 0x36
+#define RK817_CODEC_DDAC_LMT2 0x37
+#define RK817_CODEC_DDAC_MUTE_MIXCTL 0x38
+#define RK817_CODEC_DDAC_RVOLL 0x39
+#define RK817_CODEC_DDAC_RVOLR 0x3a
+#define RK817_CODEC_AHP_ANTI0 0x3b
+#define RK817_CODEC_AHP_ANTI1 0x3c
+#define RK817_CODEC_AHP_CFG0 0x3d
+#define RK817_CODEC_AHP_CFG1 0x3e
+#define RK817_CODEC_AHP_CP 0x3f
+#define RK817_CODEC_ACLASSD_CFG1 0x40
+#define RK817_CODEC_ACLASSD_CFG2 0x41
+#define RK817_CODEC_APLL_CFG0 0x42
+#define RK817_CODEC_APLL_CFG1 0x43
+#define RK817_CODEC_APLL_CFG2 0x44
+#define RK817_CODEC_APLL_CFG3 0x45
+#define RK817_CODEC_APLL_CFG4 0x46
+#define RK817_CODEC_APLL_CFG5 0x47
+#define RK817_CODEC_DI2S_CKM 0x48
+#define RK817_CODEC_DI2S_RSD 0x49
+#define RK817_CODEC_DI2S_RXCR1 0x4a
+#define RK817_CODEC_DI2S_RXCR2 0x4b
+#define RK817_CODEC_DI2S_RXCMD_TSD 0x4c
+#define RK817_CODEC_DI2S_TXCR1 0x4d
+#define RK817_CODEC_DI2S_TXCR2 0x4e
+#define RK817_CODEC_DI2S_TXCR3_TXCMD 0x4f
+
+/* RK817_CODEC_DI2S_CKM */
+#define RK817_I2S_MODE_MASK (0x1 << 0)
+#define RK817_I2S_MODE_MST (0x1 << 0)
+#define RK817_I2S_MODE_SLV (0x0 << 0)
+
+/* RK817_CODEC_DDAC_MUTE_MIXCTL */
+#define DACMT_MASK (0x1 << 0)
+#define DACMT_ENABLE (0x1 << 0)
+#define DACMT_DISABLE (0x0 << 0)
+
+/* RK817_CODEC_DI2S_RXCR2 */
+#define VDW_RX_24BITS (0x17)
+#define VDW_RX_16BITS (0x0f)
+
+/* RK817_CODEC_DI2S_TXCR2 */
+#define VDW_TX_24BITS (0x17)
+#define VDW_TX_16BITS (0x0f)
+
+/* RK817_CODEC_AMIC_CFG0 */
+#define MIC_DIFF_MASK (0x1 << 7)
+#define MIC_DIFF_DIS (0x0 << 7)
+#define MIC_DIFF_EN (0x1 << 7)
+
#define RK817_POWER_EN_REG(i) (0xb1 + (i))
#define RK817_POWER_SLP_EN_REG(i) (0xb5 + (i))
diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h
index bb8d2e276668..76a943c83c63 100644
--- a/include/linux/mfd/wcd934x/registers.h
+++ b/include/linux/mfd/wcd934x/registers.h
@@ -18,6 +18,8 @@
#define WCD934X_EFUSE_SENSE_STATE_DEF 0x10
#define WCD934X_EFUSE_SENSE_EN_MASK BIT(0)
#define WCD934X_EFUSE_SENSE_ENABLE BIT(0)
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b
#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037
#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038
#define WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039
@@ -103,21 +105,58 @@
#define WCD934X_ANA_AMIC3 0x0610
#define WCD934X_ANA_AMIC4 0x0611
#define WCD934X_ANA_MBHC_MECH 0x0614
+#define WCD934X_MBHC_L_DET_EN_MASK BIT(7)
+#define WCD934X_MBHC_L_DET_EN BIT(7)
+#define WCD934X_MBHC_GND_DET_EN_MASK BIT(6)
+#define WCD934X_MBHC_MECH_DETECT_TYPE_MASK BIT(5)
+#define WCD934X_MBHC_MECH_DETECT_TYPE_INS 1
+#define WCD934X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4)
+#define WCD934X_MBHC_HPHL_PLUG_TYPE_NO 1
+#define WCD934X_MBHC_GND_PLUG_TYPE_MASK BIT(3)
+#define WCD934X_MBHC_GND_PLUG_TYPE_NO 1
+#define WCD934X_MBHC_HSL_PULLUP_COMP_EN BIT(2)
+#define WCD934X_MBHC_HSG_PULLUP_COMP_EN BIT(1)
+#define WCD934X_MBHC_HPHL_100K_TO_GND_EN BIT(0)
#define WCD934X_ANA_MBHC_ELECT 0x0615
+#define WCD934X_ANA_MBHC_BIAS_EN_MASK BIT(0)
+#define WCD934X_ANA_MBHC_BIAS_EN BIT(0)
#define WCD934X_ANA_MBHC_ZDET 0x0616
#define WCD934X_ANA_MBHC_RESULT_1 0x0617
#define WCD934X_ANA_MBHC_RESULT_2 0x0618
#define WCD934X_ANA_MBHC_RESULT_3 0x0619
+#define WCD934X_ANA_MBHC_BTN0 0x061a
+#define WCD934X_VTH_MASK GENMASK(7, 2)
+#define WCD934X_ANA_MBHC_BTN1 0x061b
+#define WCD934X_ANA_MBHC_BTN2 0x061c
+#define WCD934X_ANA_MBHC_BTN3 0x061d
+#define WCD934X_ANA_MBHC_BTN4 0x061e
+#define WCD934X_ANA_MBHC_BTN5 0x061f
+#define WCD934X_ANA_MBHC_BTN6 0x0620
+#define WCD934X_ANA_MBHC_BTN7 0x0621
+#define WCD934X_MBHC_BTN_VTH_MASK GENMASK(7, 2)
#define WCD934X_ANA_MICB1 0x0622
#define WCD934X_MICB_VAL_MASK GENMASK(5, 0)
#define WCD934X_ANA_MICB_EN_MASK GENMASK(7, 6)
+#define WCD934X_MICB_DISABLE 0
+#define WCD934X_MICB_ENABLE 1
+#define WCD934X_MICB_PULL_UP 2
+#define WCD934X_MICB_PULL_DOWN 3
#define WCD934X_ANA_MICB_PULL_UP 0x80
#define WCD934X_ANA_MICB_ENABLE 0x40
#define WCD934X_ANA_MICB_DISABLE 0x0
#define WCD934X_ANA_MICB2 0x0623
+#define WCD934X_ANA_MICB2_ENABLE BIT(6)
+#define WCD934X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6)
+#define WCD934X_ANA_MICB2_VOUT_MASK GENMASK(5, 0)
+#define WCD934X_ANA_MICB2_RAMP 0x0624
+#define WCD934X_RAMP_EN_MASK BIT(7)
+#define WCD934X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2)
#define WCD934X_ANA_MICB3 0x0625
#define WCD934X_ANA_MICB4 0x0626
#define WCD934X_BIAS_VBG_FINE_ADJ 0x0629
+#define WCD934X_MBHC_CTL_CLK 0x0656
+#define WCD934X_MBHC_CTL_BCS 0x065a
+#define WCD934X_MBHC_STATUS_SPARE_1 0x065b
#define WCD934X_MICB1_TEST_CTL_1 0x066b
#define WCD934X_MICB1_TEST_CTL_2 0x066c
#define WCD934X_MICB2_TEST_CTL_1 0x066e
@@ -141,7 +180,11 @@
#define WCD934X_HPH_CNP_WG_CTL 0x06cc
#define WCD934X_HPH_GM3_BOOST_EN_MASK BIT(7)
#define WCD934X_HPH_GM3_BOOST_ENABLE BIT(7)
+#define WCD934X_HPH_CNP_WG_TIME 0x06cd
#define WCD934X_HPH_OCP_CTL 0x06ce
+#define WCD934X_HPH_PA_CTL2 0x06d2
+#define WCD934X_HPHPA_GND_R_MASK BIT(6)
+#define WCD934X_HPHPA_GND_L_MASK BIT(4)
#define WCD934X_HPH_L_EN 0x06d3
#define WCD934X_HPH_GAIN_SRC_SEL_MASK BIT(5)
#define WCD934X_HPH_GAIN_SRC_SEL_COMPANDER 0
@@ -152,6 +195,8 @@
#define WCD934X_HPH_OCP_DET_MASK BIT(0)
#define WCD934X_HPH_OCP_DET_ENABLE BIT(0)
#define WCD934X_HPH_OCP_DET_DISABLE 0
+#define WCD934X_HPH_R_ATEST 0x06d8
+#define WCD934X_HPHPA_GND_OVR_MASK BIT(1)
#define WCD934X_DIFF_LO_LO2_COMPANDER 0x06ea
#define WCD934X_DIFF_LO_LO1_COMPANDER 0x06eb
#define WCD934X_CLK_SYS_MCLK_PRG 0x0711
@@ -172,7 +217,19 @@
#define WCD934X_SIDO_NEW_VOUT_D_FREQ2 0x071e
#define WCD934X_SIDO_RIPPLE_FREQ_EN_MASK BIT(0)
#define WCD934X_SIDO_RIPPLE_FREQ_ENABLE BIT(0)
+#define WCD934X_MBHC_NEW_CTL_1 0x0720
+#define WCD934X_MBHC_CTL_RCO_EN_MASK BIT(7)
+#define WCD935X_MBHC_CTL_RCO_EN BIT(7)
#define WCD934X_MBHC_NEW_CTL_2 0x0721
+#define WCD934X_M_RTH_CTL_MASK GENMASK(3, 2)
+#define WCD934X_MBHC_NEW_PLUG_DETECT_CTL 0x0722
+#define WCD934X_HSDET_PULLUP_C_MASK GENMASK(7, 6)
+#define WCD934X_MBHC_NEW_ZDET_ANA_CTL 0x0723
+#define WCD934X_ZDET_RANGE_CTL_MASK GENMASK(3, 0)
+#define WCD934X_ZDET_MAXV_CTL_MASK GENMASK(6, 4)
+#define WCD934X_MBHC_NEW_ZDET_RAMP_CTL 0x0724
+#define WCD934X_MBHC_NEW_FSM_STATUS 0x0725
+#define WCD934X_MBHC_NEW_ADC_RESULT 0x0726
#define WCD934X_TX_NEW_AMIC_4_5_SEL 0x0727
#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733
#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 1358a0ceb4d0..0dcb361a98bb 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -36,6 +36,22 @@ struct snd_compr_stream;
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
+/* Describes the possible PCM format */
+/*
+ * use SND_SOC_DAI_FORMAT_xx as eash shift.
+ * see
+ * snd_soc_runtime_get_dai_fmt()
+ */
+#define SND_SOC_POSSIBLE_DAIFMT_FORMAT_SHIFT 0
+#define SND_SOC_POSSIBLE_DAIFMT_FORMAT_MASK (0xFFFF << SND_SOC_POSSIBLE_DAIFMT_FORMAT_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_I2S (1 << SND_SOC_DAI_FORMAT_I2S)
+#define SND_SOC_POSSIBLE_DAIFMT_RIGHT_J (1 << SND_SOC_DAI_FORMAT_RIGHT_J)
+#define SND_SOC_POSSIBLE_DAIFMT_LEFT_J (1 << SND_SOC_DAI_FORMAT_LEFT_J)
+#define SND_SOC_POSSIBLE_DAIFMT_DSP_A (1 << SND_SOC_DAI_FORMAT_DSP_A)
+#define SND_SOC_POSSIBLE_DAIFMT_DSP_B (1 << SND_SOC_DAI_FORMAT_DSP_B)
+#define SND_SOC_POSSIBLE_DAIFMT_AC97 (1 << SND_SOC_DAI_FORMAT_AC97)
+#define SND_SOC_POSSIBLE_DAIFMT_PDM (1 << SND_SOC_DAI_FORMAT_PDM)
+
/*
* DAI Clock gating.
*
@@ -45,6 +61,17 @@ struct snd_compr_stream;
#define SND_SOC_DAIFMT_CONT (1 << 4) /* continuous clock */
#define SND_SOC_DAIFMT_GATED (0 << 4) /* clock is gated */
+/* Describes the possible PCM format */
+/*
+ * define GATED -> CONT. GATED will be selected if both are selected.
+ * see
+ * snd_soc_runtime_get_dai_fmt()
+ */
+#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT 16
+#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_MASK (0xFFFF << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_GATED (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_CONT (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_SHIFT)
+
/*
* DAI hardware signal polarity.
*
@@ -71,6 +98,14 @@ struct snd_compr_stream;
#define SND_SOC_DAIFMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */
#define SND_SOC_DAIFMT_IB_IF (4 << 8) /* invert BCLK + FRM */
+/* Describes the possible PCM format */
+#define SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT 32
+#define SND_SOC_POSSIBLE_DAIFMT_INV_MASK (0xFFFFULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_NB_NF (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_NB_IF (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_IB_NF (0x4ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_IB_IF (0x8ULL << SND_SOC_POSSIBLE_DAIFMT_INV_SHIFT)
+
/*
* DAI hardware clock providers/consumers
*
@@ -81,7 +116,7 @@ struct snd_compr_stream;
#define SND_SOC_DAIFMT_CBP_CFP (1 << 12) /* codec clk provider & frame provider */
#define SND_SOC_DAIFMT_CBC_CFP (2 << 12) /* codec clk consumer & frame provider */
#define SND_SOC_DAIFMT_CBP_CFC (3 << 12) /* codec clk provider & frame consumer */
-#define SND_SOC_DAIFMT_CBC_CFC (4 << 12) /* codec clk consumer & frame follower */
+#define SND_SOC_DAIFMT_CBC_CFC (4 << 12) /* codec clk consumer & frame consumer */
/* previous definitions kept for backwards-compatibility, do not use in new contributions */
#define SND_SOC_DAIFMT_CBM_CFM SND_SOC_DAIFMT_CBP_CFP
@@ -89,6 +124,14 @@ struct snd_compr_stream;
#define SND_SOC_DAIFMT_CBM_CFS SND_SOC_DAIFMT_CBP_CFC
#define SND_SOC_DAIFMT_CBS_CFS SND_SOC_DAIFMT_CBC_CFC
+/* Describes the possible PCM format */
+#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT 48
+#define SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_MASK (0xFFFFULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_CBP_CFP (0x1ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_CBC_CFP (0x2ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_CBP_CFC (0x4ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
+#define SND_SOC_POSSIBLE_DAIFMT_CBC_CFC (0x8ULL << SND_SOC_POSSIBLE_DAIFMT_CLOCK_PROVIDER_SHIFT)
+
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
#define SND_SOC_DAIFMT_INV_MASK 0x0f00
@@ -131,6 +174,8 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio);
/* Digital Audio interface formatting */
+int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd);
+u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority);
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
@@ -292,6 +337,16 @@ struct snd_soc_dai_ops {
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
struct snd_soc_dai *);
+ /*
+ * Format list for auto selection.
+ * Format will be increased if priority format was
+ * not selected.
+ * see
+ * snd_soc_dai_get_fmt()
+ */
+ u64 *auto_selectable_formats;
+ int num_auto_selectable_formats;
+
/* bit field */
unsigned int no_capture_mute:1;
};
diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h
index 328cf763d9b4..4afd667e124c 100644
--- a/include/sound/soc-topology.h
+++ b/include/sound/soc-topology.h
@@ -54,7 +54,7 @@ struct snd_soc_dobj_control {
/* dynamic widget object */
struct snd_soc_dobj_widget {
- unsigned int kcontrol_type; /* kcontrol type: mixer, enum, bytes */
+ unsigned int *kcontrol_type; /* kcontrol type: mixer, enum, bytes */
};
/* generic dynamic object - all dynamic objects belong to this struct */
diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
index f22bb2bdf527..8148b0d22e88 100644
--- a/sound/soc/amd/raven/acp3x-pcm-dma.c
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -235,10 +235,6 @@ static int acp3x_dma_open(struct snd_soc_component *component,
return ret;
}
- if (!adata->play_stream && !adata->capture_stream &&
- !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
- rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
-
i2s_data->acp3x_base = adata->acp3x_base;
runtime->private_data = i2s_data;
return ret;
@@ -365,12 +361,6 @@ static int acp3x_dma_close(struct snd_soc_component *component,
}
}
- /* Disable ACP irq, when the current stream is being closed and
- * another stream is also not active.
- */
- if (!adata->play_stream && !adata->capture_stream &&
- !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
- rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
return 0;
}
diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
index 03fe93913e12..c3f0c8b7545d 100644
--- a/sound/soc/amd/raven/acp3x.h
+++ b/sound/soc/amd/raven/acp3x.h
@@ -77,6 +77,7 @@
#define ACP_POWER_OFF_IN_PROGRESS 0x03
#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
struct acp3x_platform_info {
u16 play_i2s_instance;
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index d3536fd6a124..a013a607b3d4 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -76,6 +76,19 @@ static int acp3x_reset(void __iomem *acp3x_base)
return -ETIMEDOUT;
}
+static void acp3x_enable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp3x_disable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
static int acp3x_init(struct acp3x_dev_data *adata)
{
void __iomem *acp3x_base = adata->acp3x_base;
@@ -93,6 +106,7 @@ static int acp3x_init(struct acp3x_dev_data *adata)
pr_err("ACP3x reset failed\n");
return ret;
}
+ acp3x_enable_interrupts(acp3x_base);
return 0;
}
@@ -100,6 +114,7 @@ static int acp3x_deinit(void __iomem *acp3x_base)
{
int ret;
+ acp3x_disable_interrupts(acp3x_base);
/* Reset */
ret = acp3x_reset(acp3x_base);
if (ret) {
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
index 4c2810e58dce..bd20622b0933 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -77,7 +77,6 @@ static void enable_pdm_clock(void __iomem *acp_base)
u32 pdm_clk_enable, pdm_ctrl;
pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
- pdm_ctrl = 0x00;
rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
@@ -144,9 +143,6 @@ static int stop_pdm_dma(void __iomem *acp_base)
u32 pdm_enable, pdm_dma_enable;
int timeout;
- pdm_enable = 0x00;
- pdm_dma_enable = 0x00;
-
pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
if (pdm_dma_enable & 0x01) {
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
index 9fbc3c1113cc..7745250fd743 100644
--- a/sound/soc/atmel/sam9x5_wm8731.c
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -159,7 +159,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
of_node_put(codec_np);
of_node_put(cpu_np);
- ret = snd_soc_register_card(card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev, "Platform device allocation failed\n");
goto out_put_audio;
@@ -180,7 +180,6 @@ static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct sam9x5_drvdata *priv = card->drvdata;
- snd_soc_unregister_card(card);
atmel_ssc_put_audio(priv->ssc_id);
return 0;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 2a7b3e363069..bd8583f5ebae 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -155,6 +155,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_PCM512x_I2C
imply SND_SOC_PCM512x_SPI
imply SND_SOC_RK3328
+ imply SND_SOC_RK817
imply SND_SOC_RT274
imply SND_SOC_RT286
imply SND_SOC_RT298
@@ -211,6 +212,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS6424
imply SND_SOC_TDA7419
imply SND_SOC_TFA9879
+ imply SND_SOC_TFA989X
imply SND_SOC_TLV320ADCX140
imply SND_SOC_TLV320AIC23_I2C
imply SND_SOC_TLV320AIC23_SPI
@@ -1063,6 +1065,11 @@ config SND_SOC_RK3328
tristate "Rockchip RK3328 audio CODEC"
select REGMAP_MMIO
+config SND_SOC_RK817
+ tristate "Rockchip RK817 audio CODEC"
+ depends on MFD_RK808
+ select REGMAP_I2C
+
config SND_SOC_RL6231
tristate
default y if SND_SOC_RT5514=y
@@ -1408,6 +1415,16 @@ config SND_SOC_TFA9879
tristate "NXP Semiconductors TFA9879 amplifier"
depends on I2C
+config SND_SOC_TFA989X
+ tristate "NXP/Goodix TFA989X (TFA1) amplifiers"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for NXP (now Goodix) TFA989X (TFA1 family) speaker
+ amplifiers, e.g. TFA9895.
+ Note that the driver currently bypasses the built-in "CoolFlux DSP"
+ and does not support (hardware) volume control.
+
config SND_SOC_TLV320AIC23
tristate
@@ -1525,9 +1542,13 @@ config SND_SOC_WCD9335
Qualcomm Technologies, Inc. (QTI) multimedia solutions,
including the MSM8996, MSM8976, and MSM8956 chipsets.
+config SND_SOC_WCD_MBHC
+ tristate
+
config SND_SOC_WCD934X
tristate "WCD9340/WCD9341 Codec"
depends on COMMON_CLK
+ select SND_SOC_WCD_MBHC
depends on MFD_WCD934X
help
The WCD9340/9341 is a audio codec IC Integrated in
@@ -1871,18 +1892,22 @@ config SND_SOC_TPA6130A2
config SND_SOC_LPASS_WSA_MACRO
depends on COMMON_CLK
+ select REGMAP_MMIO
tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)"
config SND_SOC_LPASS_VA_MACRO
depends on COMMON_CLK
+ select REGMAP_MMIO
tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)"
config SND_SOC_LPASS_RX_MACRO
depends on COMMON_CLK
+ select REGMAP_MMIO
tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)"
config SND_SOC_LPASS_TX_MACRO
depends on COMMON_CLK
+ select REGMAP_MMIO
tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)"
endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0efdba609048..9d17a374a825 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -166,6 +166,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rk3328-objs := rk3328_codec.o
+snd-soc-rk817-objs := rk817_codec.o
snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt1011-objs := rt1011.o
@@ -229,6 +230,7 @@ snd-soc-tas6424-objs := tas6424.o
snd-soc-tda7419-objs := tda7419.o
snd-soc-tas2770-objs := tas2770.o
snd-soc-tfa9879-objs := tfa9879.o
+snd-soc-tfa989x-objs := tfa989x.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
@@ -250,6 +252,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda1334-objs := uda1334.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o
snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o
snd-soc-wl1273-objs := wl1273.o
@@ -487,6 +490,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
+obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
@@ -551,6 +555,7 @@ obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
+obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o
@@ -572,6 +577,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index fe208cfdd3ba..4d2e78101f28 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -539,6 +539,15 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static u64 ak4613_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4613_dai_ops = {
.startup = ak4613_dai_startup,
.shutdown = ak4613_dai_shutdown,
@@ -546,6 +555,8 @@ static const struct snd_soc_dai_ops ak4613_dai_ops = {
.set_fmt = ak4613_dai_set_fmt,
.trigger = ak4613_dai_trigger,
.hw_params = ak4613_dai_hw_params,
+ .auto_selectable_formats = &ak4613_dai_formats,
+ .num_auto_selectable_formats = 1,
};
#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
index 34aed80db0eb..37d4600b6f2c 100644
--- a/sound/soc/codecs/ak5558.c
+++ b/sound/soc/codecs/ak5558.c
@@ -307,7 +307,7 @@ static struct snd_soc_dai_driver ak5558_dai = {
};
static struct snd_soc_dai_driver ak5552_dai = {
- .name = "ak5558-aif",
+ .name = "ak5552-aif",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
diff --git a/sound/soc/codecs/cirrus_legacy.h b/sound/soc/codecs/cirrus_legacy.h
new file mode 100644
index 000000000000..87c6fd79290d
--- /dev/null
+++ b/sound/soc/codecs/cirrus_legacy.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Some small helpers for older Cirrus Logic parts.
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+static inline int cirrus_read_device_id(struct regmap *regmap, unsigned int reg)
+{
+ u8 devid[3];
+ int ret;
+
+ ret = regmap_bulk_read(regmap, reg, devid, ARRAY_SIZE(devid));
+ if (ret < 0)
+ return ret;
+
+ return ((devid[0] & 0xFF) << 12) |
+ ((devid[1] & 0xFF) << 4) |
+ ((devid[2] & 0xF0) >> 4);
+}
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index f4067230ac42..933e3d627e5f 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -30,6 +30,7 @@
#include <dt-bindings/sound/cs35l32.h>
#include "cs35l32.h"
+#include "cirrus_legacy.h"
#define CS35L32_NUM_SUPPLIES 2
static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
@@ -261,6 +262,9 @@ static const struct regmap_config cs35l32_regmap = {
.readable_reg = cs35l32_readable_register,
.precious_reg = cs35l32_precious_register,
.cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
@@ -348,8 +352,7 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
struct cs35l32_private *cs35l32;
struct cs35l32_platform_data *pdata =
dev_get_platdata(&i2c_client->dev);
- int ret, i;
- unsigned int devid = 0;
+ int ret, i, devid;
unsigned int reg;
cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
@@ -404,40 +407,40 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
/* Reset the Device */
cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs35l32->reset_gpio))
- return PTR_ERR(cs35l32->reset_gpio);
+ if (IS_ERR(cs35l32->reset_gpio)) {
+ ret = PTR_ERR(cs35l32->reset_gpio);
+ goto err_supplies;
+ }
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
/* initialize codec */
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
if (devid != CS35L32_CHIP_ID) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS35L32 Device ID (%X). Expected %X\n",
devid, CS35L32_CHIP_ID);
- return ret;
+ goto err_disable;
}
ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ goto err_disable;
}
ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
ARRAY_SIZE(cs35l32_monitor_patch));
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
- return ret;
+ goto err_disable;
}
dev_info(&i2c_client->dev,
@@ -478,7 +481,7 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
CS35L32_PDN_AMP);
/* Clear MCLK Error Bit since we don't have the clock yet */
- ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+ regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
ret = devm_snd_soc_register_component(&i2c_client->dev,
&soc_component_dev_cs35l32, cs35l32_dai,
@@ -489,6 +492,8 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
return 0;
err_disable:
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+err_supplies:
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
cs35l32->supplies);
return ret;
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 7ad7b733af9b..2a6f5e46d031 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -34,6 +34,7 @@
#include <linux/of_irq.h>
#include "cs35l33.h"
+#include "cirrus_legacy.h"
#define CS35L33_BOOT_DELAY 50
@@ -1190,17 +1191,18 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
regcache_cache_only(cs35l33->regmap, false);
/* initialize codec */
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs35l33->regmap, CS35L33_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_enable;
+ }
if (devid != CS35L33_CHIP_ID) {
dev_err(&i2c_client->dev,
"CS35L33 Device ID (%X). Expected ID %X\n",
devid, CS35L33_CHIP_ID);
+ ret = -EINVAL;
goto err_enable;
}
@@ -1242,6 +1244,8 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
return 0;
err_enable:
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
regulator_bulk_disable(cs35l33->num_core_supplies,
cs35l33->core_supplies);
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index 110ee2d06358..ed678241c22b 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -34,6 +34,7 @@
#include <sound/cs35l34.h>
#include "cs35l34.h"
+#include "cirrus_legacy.h"
#define PDN_DONE_ATTEMPTS 10
#define CS35L34_START_DELAY 50
@@ -800,6 +801,9 @@ static struct regmap_config cs35l34_regmap = {
.readable_reg = cs35l34_readable_register,
.precious_reg = cs35l34_precious_register,
.cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
@@ -996,9 +1000,8 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
struct cs35l34_private *cs35l34;
struct cs35l34_platform_data *pdata =
dev_get_platdata(&i2c_client->dev);
- int i;
+ int i, devid;
int ret;
- unsigned int devid = 0;
unsigned int reg;
cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL);
@@ -1039,13 +1042,15 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
} else {
pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto err_regulator;
+ }
if (i2c_client->dev.of_node) {
ret = cs35l34_handle_of_data(i2c_client, pdata);
if (ret != 0)
- return ret;
+ goto err_regulator;
}
cs35l34->pdata = *pdata;
@@ -1059,33 +1064,34 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"reset-gpios", GPIOD_OUT_LOW);
- if (IS_ERR(cs35l34->reset_gpio))
- return PTR_ERR(cs35l34->reset_gpio);
+ if (IS_ERR(cs35l34->reset_gpio)) {
+ ret = PTR_ERR(cs35l34->reset_gpio);
+ goto err_regulator;
+ }
gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
msleep(CS35L34_START_DELAY);
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs35l34->regmap, CS35L34_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
if (devid != CS35L34_CHIP_ID) {
dev_err(&i2c_client->dev,
"CS35l34 Device ID (%X). Expected ID %X\n",
devid, CS35L34_CHIP_ID);
ret = -ENODEV;
- goto err_regulator;
+ goto err_reset;
}
ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- goto err_regulator;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
@@ -1110,11 +1116,13 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
if (ret < 0) {
dev_err(&i2c_client->dev,
"%s: Register component failed\n", __func__);
- goto err_regulator;
+ goto err_reset;
}
return 0;
+err_reset:
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
err_regulator:
regulator_bulk_disable(cs35l34->num_core_supplies,
cs35l34->core_supplies);
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index f20ed838b958..7a5588f1df01 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -33,6 +33,7 @@
#include <linux/completion.h>
#include "cs35l35.h"
+#include "cirrus_legacy.h"
/*
* Some fields take zero as a valid value so use a high bit flag that won't
@@ -367,16 +368,16 @@ static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
struct snd_soc_component *component = codec_dai->component;
struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
- cs35l35->slave_mode = false;
+ cs35l35->clock_consumer = false;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
- cs35l35->slave_mode = true;
+ cs35l35->clock_consumer = true;
break;
default:
return -EINVAL;
@@ -495,10 +496,10 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
* the Class H algorithm does not enable weak-drive operation for
* nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
*/
- errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+ errata_chk = (clk_ctl & CS35L35_SP_RATE_MASK) >> CS35L35_SP_RATE_SHIFT;
if (classh->classh_wk_fet_disable == 0x00 &&
- (errata_chk == 0x01 || errata_chk == 0x03)) {
+ (errata_chk == 0x01 || errata_chk == 0x02)) {
ret = regmap_update_bits(cs35l35->regmap,
CS35L35_CLASS_H_FET_DRIVE_CTL,
CS35L35_CH_WKFET_DEL_MASK,
@@ -555,8 +556,8 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
}
sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
- /* Only certain ratios are supported in I2S Slave Mode */
- if (cs35l35->slave_mode) {
+ /* Only certain ratios supported when device is a clock consumer */
+ if (cs35l35->clock_consumer) {
switch (sp_sclks) {
case CS35L35_SP_SCLKS_32FS:
case CS35L35_SP_SCLKS_48FS:
@@ -567,7 +568,7 @@ static int cs35l35_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
} else {
- /* Only certain ratios supported in I2S MASTER Mode */
+ /* Only certain ratios supported when device is a clock provider */
switch (sp_sclks) {
case CS35L35_SP_SCLKS_32FS:
case CS35L35_SP_SCLKS_64FS:
@@ -1471,9 +1472,8 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
struct cs35l35_private *cs35l35;
struct device *dev = &i2c_client->dev;
struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
- int i;
+ int i, devid;
int ret;
- unsigned int devid = 0;
unsigned int reg;
cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
@@ -1552,13 +1552,12 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
goto err;
}
/* initialize codec */
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs35l35->regmap, CS35L35_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err;
+ }
if (devid != CS35L35_CHIP_ID) {
dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n",
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
index ffb154cd962c..5e4509f41b32 100644
--- a/sound/soc/codecs/cs35l35.h
+++ b/sound/soc/codecs/cs35l35.h
@@ -168,6 +168,7 @@
#define CS35L35_SP_SCLKS_48FS 0x0B
#define CS35L35_SP_SCLKS_64FS 0x0F
#define CS35L35_SP_RATE_MASK 0xC0
+#define CS35L35_SP_RATE_SHIFT 6
#define CS35L35_PDN_BST_MASK 0x06
#define CS35L35_PDN_BST_FETON_SHIFT 1
@@ -282,7 +283,7 @@ struct cs35l35_private {
int sclk;
bool pdm_mode;
bool i2s_mode;
- bool slave_mode;
+ bool clock_consumer;
/* GPIO for /RST */
struct gpio_desc *reset_gpio;
struct completion pdn_done;
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index a038bcec2d17..d83c1b318c1c 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -756,14 +756,14 @@ static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai,
{
struct cs35l36_private *cs35l36 =
snd_soc_component_get_drvdata(component_dai->component);
- unsigned int asp_fmt, lrclk_fmt, sclk_fmt, slave_mode, clk_frc;
+ unsigned int asp_fmt, lrclk_fmt, sclk_fmt, clock_provider, clk_frc;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- slave_mode = 1;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ clock_provider = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- slave_mode = 0;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ clock_provider = 0;
break;
default:
return -EINVAL;
@@ -771,10 +771,10 @@ static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai,
regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL,
CS35L36_SCLK_MSTR_MASK,
- slave_mode << CS35L36_SCLK_MSTR_SHIFT);
+ clock_provider << CS35L36_SCLK_MSTR_SHIFT);
regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL,
CS35L36_LRCLK_MSTR_MASK,
- slave_mode << CS35L36_LRCLK_MSTR_SHIFT);
+ clock_provider << CS35L36_LRCLK_MSTR_SHIFT);
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
case SND_SOC_DAIFMT_CONT:
@@ -1156,7 +1156,7 @@ static int cs35l36_component_probe(struct snd_soc_component *component)
{
struct cs35l36_private *cs35l36 =
snd_soc_component_get_drvdata(component);
- int ret = 0;
+ int ret;
if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) {
regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL,
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index d76be44f46b4..cffd6111afac 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -573,7 +573,7 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct cs4265_private *cs4265;
- int ret = 0;
+ int ret;
unsigned int devid = 0;
unsigned int reg;
@@ -602,6 +602,11 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, cs4265);
ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ret = -ENODEV;
@@ -616,10 +621,9 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
- ret = devm_snd_soc_register_component(&i2c_client->dev,
+ return devm_snd_soc_register_component(&i2c_client->dev,
&soc_component_cs4265, cs4265_dai,
ARRAY_SIZE(cs4265_dai));
- return ret;
}
static const struct of_device_id cs4265_of_match[] = {
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index bf982e145e94..eff013f295be 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -19,11 +19,11 @@
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <sound/core.h>
@@ -36,6 +36,7 @@
#include <dt-bindings/sound/cs42l42.h>
#include "cs42l42.h"
+#include "cirrus_legacy.h"
static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_FRZ_CTL, 0x00 },
@@ -399,6 +400,9 @@ static const struct regmap_config cs42l42_regmap = {
.reg_defaults = cs42l42_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults),
.cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, false);
@@ -518,26 +522,33 @@ static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
{ "SDOUT2", NULL, "ASP TX EN" },
};
+static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ cs42l42->jack = jk;
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK | CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) | (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT));
+
+ return 0;
+}
+
static int cs42l42_component_probe(struct snd_soc_component *component)
{
- struct cs42l42_private *cs42l42 =
- (struct cs42l42_private *)snd_soc_component_get_drvdata(component);
- struct snd_soc_card *crd = component->card;
- int ret = 0;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
cs42l42->component = component;
- ret = snd_soc_card_jack_new(crd, "CS42L42 Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cs42l42->jack, NULL, 0);
- if (ret < 0)
- dev_err(component->dev, "Cannot create CS42L42 Headset: %d\n", ret);
-
- return ret;
+ return 0;
}
static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
.probe = cs42l42_component_probe,
+ .set_jack = cs42l42_set_jack,
.dapm_widgets = cs42l42_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
.dapm_routes = cs42l42_audio_map,
@@ -578,6 +589,7 @@ struct cs42l42_pll_params {
u8 pll_divout;
u32 mclk_int;
u8 pll_cal_ratio;
+ u8 n;
};
/*
@@ -585,21 +597,23 @@ struct cs42l42_pll_params {
* Table 4-5 from the Datasheet
*/
static const struct cs42l42_pll_params pll_ratio_table[] = {
- { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125 },
- { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
- { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
- { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
- { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96 },
- { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94 },
- { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
- { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
- { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
- { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0 },
- { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0 },
- { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0 },
- { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0 },
- { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0 },
- { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0 }
+ { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2},
+ { 2304000, 0, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2},
+ { 2400000, 0, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2},
+ { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1},
+ { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1},
+ { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1},
+ { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1},
+ { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1},
+ { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0, 1},
+ { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0, 1},
+ { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0, 1},
+ { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0, 1},
+ { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0, 1},
+ { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0, 1}
};
static int cs42l42_pll_config(struct snd_soc_component *component)
@@ -735,8 +749,12 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
snd_soc_component_update_bits(component,
CS42L42_PLL_CTL3,
CS42L42_PLL_DIVOUT_MASK,
- pll_ratio_table[i].pll_divout
+ (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n)
<< CS42L42_PLL_DIVOUT_SHIFT);
+ if (pll_ratio_table[i].n != 1)
+ cs42l42->pll_divout = pll_ratio_table[i].pll_divout;
+ else
+ cs42l42->pll_divout = 0;
snd_soc_component_update_bits(component,
CS42L42_PLL_CAL_RATIO,
CS42L42_PLL_CAL_RATIO_MASK,
@@ -891,6 +909,16 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
if ((cs42l42->bclk < 11289600) && (cs42l42->sclk < 11289600)) {
snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
CS42L42_PLL_START_MASK, 1);
+
+ if (cs42l42->pll_divout) {
+ usleep_range(CS42L42_PLL_DIVOUT_TIME_US,
+ CS42L42_PLL_DIVOUT_TIME_US * 2);
+ snd_soc_component_update_bits(component, CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ cs42l42->pll_divout <<
+ CS42L42_PLL_DIVOUT_SHIFT);
+ }
+
ret = regmap_read_poll_timeout(cs42l42->regmap,
CS42L42_PLL_LOCK_STATUS,
regval,
@@ -1025,7 +1053,7 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
CS42L42_AUTO_HSBIAS_HIZ_MASK |
CS42L42_TIP_SENSE_EN_MASK |
CS42L42_HSBIAS_SENSE_TRIP_MASK,
- (1 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
(1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
(0 << CS42L42_TIP_SENSE_EN_SHIFT) |
(3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
@@ -1410,11 +1438,11 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
switch(cs42l42->hs_type){
case CS42L42_PLUG_CTIA:
case CS42L42_PLUG_OMTP:
- snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADSET,
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET,
SND_JACK_HEADSET);
break;
case CS42L42_PLUG_HEADPHONE:
- snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADPHONE,
+ snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADPHONE,
SND_JACK_HEADPHONE);
break;
default:
@@ -1442,14 +1470,18 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
switch(cs42l42->hs_type){
case CS42L42_PLUG_CTIA:
case CS42L42_PLUG_OMTP:
- snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADSET);
+ snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET);
break;
case CS42L42_PLUG_HEADPHONE:
- snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADPHONE);
+ snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADPHONE);
break;
default:
break;
}
+ snd_soc_jack_report(cs42l42->jack, 0,
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
dev_dbg(component->dev, "Unplug event\n");
}
break;
@@ -1461,7 +1493,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
}
/* Check button detect status */
- if ((~masks[7]) & irq_params_table[7].mask) {
+ if (cs42l42->plug_state == CS42L42_TS_PLUG && ((~masks[7]) & irq_params_table[7].mask)) {
if (!(current_button_status &
CS42L42_M_HSBIAS_HIZ_MASK)) {
@@ -1472,7 +1504,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
report = cs42l42_handle_button_press(cs42l42);
}
- snd_soc_jack_report(&cs42l42->jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ snd_soc_jack_report(cs42l42->jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
}
@@ -1579,8 +1611,8 @@ static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
CS42L42_TS_UNPLUG_MASK,
(1 << CS42L42_RS_PLUG_SHIFT) |
(1 << CS42L42_RS_UNPLUG_SHIFT) |
- (0 << CS42L42_TS_PLUG_SHIFT) |
- (0 << CS42L42_TS_UNPLUG_SHIFT));
+ (1 << CS42L42_TS_PLUG_SHIFT) |
+ (1 << CS42L42_TS_UNPLUG_SHIFT));
}
static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
@@ -1630,17 +1662,15 @@ static const unsigned int threshold_defaults[] = {
CS42L42_HS_DET_LEVEL_1
};
-static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
+static int cs42l42_handle_device_data(struct device *dev,
struct cs42l42_private *cs42l42)
{
- struct device_node *np = i2c_client->dev.of_node;
unsigned int val;
- unsigned int thresholds[CS42L42_NUM_BIASES];
+ u32 thresholds[CS42L42_NUM_BIASES];
int ret;
int i;
- ret = of_property_read_u32(np, "cirrus,ts-inv", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-inv", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_INV_EN:
@@ -1648,7 +1678,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_inv = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-inv DT value %d\n",
val);
cs42l42->ts_inv = CS42L42_TS_INV_DIS;
@@ -1661,8 +1691,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
CS42L42_TS_INV_MASK,
(cs42l42->ts_inv << CS42L42_TS_INV_SHIFT));
- ret = of_property_read_u32(np, "cirrus,ts-dbnc-rise", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_DBNCE_0:
@@ -1676,7 +1705,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_dbnc_rise = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-dbnc-rise DT value %d\n",
val);
cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
@@ -1690,8 +1719,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(cs42l42->ts_dbnc_rise <<
CS42L42_TS_RISE_DBNCE_TIME_SHIFT));
- ret = of_property_read_u32(np, "cirrus,ts-dbnc-fall", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,ts-dbnc-fall", &val);
if (!ret) {
switch (val) {
case CS42L42_TS_DBNCE_0:
@@ -1705,7 +1733,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->ts_dbnc_fall = val;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,ts-dbnc-fall DT value %d\n",
val);
cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
@@ -1719,13 +1747,12 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(cs42l42->ts_dbnc_fall <<
CS42L42_TS_FALL_DBNCE_TIME_SHIFT));
- ret = of_property_read_u32(np, "cirrus,btn-det-init-dbnce", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,btn-det-init-dbnce", &val);
if (!ret) {
if (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX)
cs42l42->btn_det_init_dbnce = val;
else {
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,btn-det-init-dbnce DT value %d\n",
val);
cs42l42->btn_det_init_dbnce =
@@ -1736,14 +1763,13 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
}
- ret = of_property_read_u32(np, "cirrus,btn-det-event-dbnce", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,btn-det-event-dbnce", &val);
if (!ret) {
if (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX)
cs42l42->btn_det_event_dbnce = val;
else {
- dev_err(&i2c_client->dev,
- "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
+ dev_err(dev,
+ "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
cs42l42->btn_det_event_dbnce =
CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
}
@@ -1752,19 +1778,17 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
}
- ret = of_property_read_u32_array(np, "cirrus,bias-lvls",
- (u32 *)thresholds, CS42L42_NUM_BIASES);
-
+ ret = device_property_read_u32_array(dev, "cirrus,bias-lvls",
+ thresholds, ARRAY_SIZE(thresholds));
if (!ret) {
for (i = 0; i < CS42L42_NUM_BIASES; i++) {
if (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX)
cs42l42->bias_thresholds[i] = thresholds[i];
else {
- dev_err(&i2c_client->dev,
- "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
+ dev_err(dev,
+ "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
thresholds[i]);
- cs42l42->bias_thresholds[i] =
- threshold_defaults[i];
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
}
}
} else {
@@ -1772,8 +1796,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->bias_thresholds[i] = threshold_defaults[i];
}
- ret = of_property_read_u32(np, "cirrus,hs-bias-ramp-rate", &val);
-
+ ret = device_property_read_u32(dev, "cirrus,hs-bias-ramp-rate", &val);
if (!ret) {
switch (val) {
case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL:
@@ -1793,7 +1816,7 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3;
break;
default:
- dev_err(&i2c_client->dev,
+ dev_err(dev,
"Wrong cirrus,hs-bias-ramp-rate DT value %d\n",
val);
cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
@@ -1809,6 +1832,11 @@ static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
(cs42l42->hs_bias_ramp_rate <<
CS42L42_HSBIAS_RAMP_SHIFT));
+ if (device_property_read_bool(dev, "cirrus,hs-bias-sense-disable"))
+ cs42l42->hs_bias_sense_en = 0;
+ else
+ cs42l42->hs_bias_sense_en = 1;
+
return 0;
}
@@ -1816,8 +1844,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct cs42l42_private *cs42l42;
- int ret, i;
- unsigned int devid = 0;
+ int ret, i, devid;
unsigned int reg;
cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private),
@@ -1880,14 +1907,12 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
"Failed to request IRQ: %d\n", ret);
/* initialize codec */
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
if (devid != CS42L42_CHIP_ID) {
ret = -ENODEV;
@@ -1923,11 +1948,9 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
(1 << CS42L42_ADC_PDN_SHIFT) |
(0 << CS42L42_PDN_ALL_SHIFT));
- if (i2c_client->dev.of_node) {
- ret = cs42l42_handle_device_data(i2c_client, cs42l42);
- if (ret != 0)
- goto err_disable;
- }
+ ret = cs42l42_handle_device_data(&i2c_client->dev, cs42l42);
+ if (ret != 0)
+ goto err_disable;
/* Setup headset detection */
cs42l42_setup_hs_type_detect(cs42l42);
@@ -2006,12 +2029,21 @@ static const struct dev_pm_ops cs42l42_runtime_pm = {
NULL)
};
+#ifdef CONFIG_OF
static const struct of_device_id cs42l42_of_match[] = {
{ .compatible = "cirrus,cs42l42", },
- {},
+ {}
};
MODULE_DEVICE_TABLE(of, cs42l42_of_match);
+#endif
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs42l42_acpi_match[] = {
+ {"10134242", 0,},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match);
+#endif
static const struct i2c_device_id cs42l42_id[] = {
{"cs42l42", 0},
@@ -2024,7 +2056,8 @@ static struct i2c_driver cs42l42_i2c_driver = {
.driver = {
.name = "cs42l42",
.pm = &cs42l42_runtime_pm,
- .of_match_table = cs42l42_of_match,
+ .of_match_table = of_match_ptr(cs42l42_of_match),
+ .acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
},
.id_table = cs42l42_id,
.probe = cs42l42_i2c_probe,
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index 36b763f0d1a0..7bf05ff05f74 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -755,6 +755,7 @@
#define CS42L42_NUM_SUPPLIES 5
#define CS42L42_BOOT_TIME_US 3000
+#define CS42L42_PLL_DIVOUT_TIME_US 800
#define CS42L42_CLOCK_SWITCH_DELAY_US 150
#define CS42L42_PLL_LOCK_POLL_US 250
#define CS42L42_PLL_LOCK_TIMEOUT_US 1250
@@ -773,10 +774,11 @@ struct cs42l42_private {
struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
struct gpio_desc *reset_gpio;
struct completion pdn_done;
- struct snd_soc_jack jack;
+ struct snd_soc_jack *jack;
int bclk;
u32 sclk;
u32 srate;
+ u8 pll_divout;
u8 plug_state;
u8 hs_type;
u8 ts_inv;
@@ -787,6 +789,7 @@ struct cs42l42_private {
u8 bias_thresholds[CS42L42_NUM_BIASES];
u8 hs_bias_ramp_rate;
u8 hs_bias_ramp_time;
+ u8 hs_bias_sense_en;
u8 stream_use;
};
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 796b894c390f..80161151b3f2 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -957,9 +957,8 @@ static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l52_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
long int time;
@@ -974,7 +973,7 @@ static ssize_t cs42l52_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+static DEVICE_ATTR_WO(beep);
static void cs42l52_init_beep(struct snd_soc_component *component)
{
@@ -1093,7 +1092,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
struct cs42l52_private *cs42l52;
struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
int ret;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int reg;
u32 val32;
@@ -1163,6 +1162,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
ret);
ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
devid = reg & CS42L52_CHIP_ID_MASK;
if (devid != CS42L52_CHIP_ID) {
ret = -ENODEV;
@@ -1199,11 +1203,8 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
CS42L52_IFACE_CTL2_BIAS_LVL,
cs42l52->pdata.micbias_lvl);
- ret = devm_snd_soc_register_component(&i2c_client->dev,
+ return devm_snd_soc_register_component(&i2c_client->dev,
&soc_component_dev_cs42l52, &cs42l52_dai, 1);
- if (ret < 0)
- return ret;
- return 0;
}
static const struct of_device_id cs42l52_of_match[] = {
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index c44a5cdb796e..3cf8a0b4478c 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1021,9 +1021,8 @@ static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l56_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct cs42l56_private *cs42l56 = dev_get_drvdata(dev);
long int time;
@@ -1038,7 +1037,7 @@ static ssize_t cs42l56_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+static DEVICE_ATTR_WO(beep);
static void cs42l56_init_beep(struct snd_soc_component *component)
{
@@ -1175,7 +1174,7 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
struct cs42l56_platform_data *pdata =
dev_get_platdata(&i2c_client->dev);
int ret, i;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int alpha_rev, metal_rev;
unsigned int reg;
@@ -1245,6 +1244,11 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
}
ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+ if (ret) {
+ dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
devid = reg & CS42L56_CHIP_ID_MASK;
if (devid != CS42L56_DEVID) {
dev_err(&i2c_client->dev,
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index c3f974ec78e5..018463f34e12 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -27,6 +27,7 @@
#include <sound/tlv.h>
#include <sound/cs42l73.h>
#include "cs42l73.h"
+#include "cirrus_legacy.h"
struct sp_config {
u8 spc, mmcc, spfs;
@@ -1268,6 +1269,9 @@ static const struct regmap_config cs42l73_regmap = {
.volatile_reg = cs42l73_volatile_register,
.readable_reg = cs42l73_readable_register,
.cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
@@ -1275,8 +1279,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
{
struct cs42l73_private *cs42l73;
struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev);
- int ret;
- unsigned int devid = 0;
+ int ret, devid;
unsigned int reg;
u32 val32;
@@ -1326,27 +1329,25 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
}
/* initialize codec */
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs42l73->regmap, CS42L73_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_reset;
+ }
if (devid != CS42L73_DEVID) {
ret = -ENODEV;
dev_err(&i2c_client->dev,
"CS42L73 Device ID (%X). Expected %X\n",
devid, CS42L73_DEVID);
- return ret;
+ goto err_reset;
}
ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
@@ -1356,8 +1357,14 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
&soc_component_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
if (ret < 0)
- return ret;
+ goto err_reset;
+
return 0;
+
+err_reset:
+ gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
+
+ return ret;
}
static const struct of_device_id cs42l73_of_match[] = {
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 80bc7c10ed75..7c521bd6b040 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -36,6 +36,7 @@
#include <sound/jack.h>
#include "cs43130.h"
+#include "cirrus_legacy.h"
static const struct reg_default cs43130_reg_defaults[] = {
{CS43130_SYS_CLK_CTL_1, 0x06},
@@ -1671,14 +1672,14 @@ static int cs43130_show_dc(struct device *dev, char *buf, u8 ch)
cs43130->hpload_dc[ch]);
}
-static ssize_t cs43130_show_dc_l(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t hpload_dc_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return cs43130_show_dc(dev, buf, HP_LEFT);
}
-static ssize_t cs43130_show_dc_r(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t hpload_dc_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return cs43130_show_dc(dev, buf, HP_RIGHT);
}
@@ -1718,22 +1719,22 @@ static int cs43130_show_ac(struct device *dev, char *buf, u8 ch)
}
}
-static ssize_t cs43130_show_ac_l(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t hpload_ac_l_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return cs43130_show_ac(dev, buf, HP_LEFT);
}
-static ssize_t cs43130_show_ac_r(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t hpload_ac_r_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return cs43130_show_ac(dev, buf, HP_RIGHT);
}
-static DEVICE_ATTR(hpload_dc_l, 0444, cs43130_show_dc_l, NULL);
-static DEVICE_ATTR(hpload_dc_r, 0444, cs43130_show_dc_r, NULL);
-static DEVICE_ATTR(hpload_ac_l, 0444, cs43130_show_ac_l, NULL);
-static DEVICE_ATTR(hpload_ac_r, 0444, cs43130_show_ac_r, NULL);
+static DEVICE_ATTR_RO(hpload_dc_l);
+static DEVICE_ATTR_RO(hpload_dc_r);
+static DEVICE_ATTR_RO(hpload_ac_l);
+static DEVICE_ATTR_RO(hpload_ac_r);
static struct reg_sequence hp_en_cal_seq[] = {
{CS43130_INT_MASK_4, CS43130_INT_MASK_ALL},
@@ -2424,9 +2425,8 @@ static int cs43130_i2c_probe(struct i2c_client *client,
{
struct cs43130_private *cs43130;
int ret;
- unsigned int devid = 0;
unsigned int reg;
- int i;
+ int i, devid;
cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL);
if (!cs43130)
@@ -2464,20 +2464,21 @@ static int cs43130_i2c_probe(struct i2c_client *client,
cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset", GPIOD_OUT_LOW);
- if (IS_ERR(cs43130->reset_gpio))
- return PTR_ERR(cs43130->reset_gpio);
+ if (IS_ERR(cs43130->reset_gpio)) {
+ ret = PTR_ERR(cs43130->reset_gpio);
+ goto err_supplies;
+ }
gpiod_set_value_cansleep(cs43130->reset_gpio, 1);
usleep_range(2000, 2050);
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_AB, &reg);
-
- devid = (reg & 0xFF) << 12;
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
- ret = regmap_read(cs43130->regmap, CS43130_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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);
+ goto err;
+ }
switch (devid) {
case CS43130_CHIP_ID:
@@ -2517,7 +2518,7 @@ static int cs43130_i2c_probe(struct i2c_client *client,
"cs43130", cs43130);
if (ret != 0) {
dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
- return ret;
+ goto err;
}
cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
@@ -2576,7 +2577,13 @@ static int cs43130_i2c_probe(struct i2c_client *client,
CS43130_XSP_3ST_MASK, 0);
return 0;
+
err:
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+err_supplies:
+ regulator_bulk_disable(ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+
return ret;
}
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index eaabbb56a173..6b6d08816024 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -1178,7 +1178,7 @@ static unsigned int cs47l24_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_4L,
};
-static struct snd_compress_ops cs47l24_compress_ops = {
+static const struct snd_compress_ops cs47l24_compress_ops = {
.open = cs47l24_open,
.free = wm_adsp_compr_free,
.set_params = wm_adsp_compr_set_params,
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index 3d67cbf9eaaa..f2087bd38dbc 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -20,6 +20,7 @@
#include <sound/tlv.h>
#include "cs53l30.h"
+#include "cirrus_legacy.h"
#define CS53L30_NUM_SUPPLIES 2
static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
@@ -912,6 +913,9 @@ static struct regmap_config cs53l30_regmap = {
.writeable_reg = cs53l30_writeable_register,
.readable_reg = cs53l30_readable_register,
.cache_type = REGCACHE_RBTREE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs53l30_i2c_probe(struct i2c_client *client,
@@ -920,9 +924,8 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
const struct device_node *np = client->dev.of_node;
struct device *dev = &client->dev;
struct cs53l30_private *cs53l30;
- unsigned int devid = 0;
unsigned int reg;
- int ret = 0, i;
+ int ret = 0, i, devid;
u8 val;
cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
@@ -951,7 +954,7 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
GPIOD_OUT_LOW);
if (IS_ERR(cs53l30->reset_gpio)) {
ret = PTR_ERR(cs53l30->reset_gpio);
- goto error;
+ goto error_supplies;
}
gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
@@ -968,14 +971,12 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
}
/* Initialize codec */
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
- devid = reg << 12;
-
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
- devid |= reg << 4;
-
- ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ devid = cirrus_read_device_id(cs53l30->regmap, CS53L30_DEVID_AB);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto error;
+ }
if (devid != CS53L30_DEVID) {
ret = -ENODEV;
@@ -1037,6 +1038,8 @@ static int cs53l30_i2c_probe(struct i2c_client *client,
return 0;
error:
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+error_supplies:
regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies);
return ret;
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index bd3c523a8617..13009d08b09a 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -2181,10 +2181,7 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
ret);
goto err;
}
-
- da7219->dai_clks[i] = devm_clk_hw_get_clk(dev, dai_clk_hw, NULL);
- if (IS_ERR(da7219->dai_clks[i]))
- return PTR_ERR(da7219->dai_clks[i]);
+ da7219->dai_clks[i] = dai_clk_hw->clk;
/* For DT setup onecell data, otherwise create lookup */
if (np) {
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index a67c92032e11..e0460a9bd17a 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -685,6 +685,25 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction)
return -ENOTSUPP;
}
+/*
+ * This driver can select all SND_SOC_DAIFMT_CBx_CFx,
+ * but need to be selected from Sound Card, not be auto selected.
+ * Because it might be used from other driver.
+ * For example,
+ * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+ */
+static u64 hdmi_codec_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_AC97;
+
static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
.startup = hdmi_codec_startup,
.shutdown = hdmi_codec_shutdown,
@@ -692,6 +711,8 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
.prepare = hdmi_codec_prepare,
.set_fmt = hdmi_codec_i2s_set_fmt,
.mute_stream = hdmi_codec_mute,
+ .auto_selectable_formats = &hdmi_codec_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index b0ebfc8d180c..3622961f7c2c 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -2628,7 +2628,7 @@ static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w,
break;
default:
break;
- };
+ }
return 0;
}
@@ -3579,6 +3579,7 @@ static const struct of_device_id rx_macro_dt_match[] = {
{ .compatible = "qcom,sm8250-lpass-rx-macro" },
{ }
};
+MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
static struct platform_driver rx_macro_driver = {
.driver = {
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index acd2fbc0ca7c..27a0d5defd27 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -1846,6 +1846,7 @@ static const struct of_device_id tx_macro_dt_match[] = {
{ .compatible = "qcom,sm8250-lpass-tx-macro" },
{ }
};
+MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
static struct platform_driver tx_macro_driver = {
.driver = {
.name = "tx_macro",
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 1a7fa5492f28..d3ac318fd6b6 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1727,6 +1727,10 @@ static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w,
val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK;
ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1;
break;
+ default:
+ dev_err(component->dev, "%s: Invalid shift %u\n",
+ __func__, w->shift);
+ return -EINVAL;
}
if (wsa->ec_hq[ec_tx]) {
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 4be24e7f51c8..f8e49e45ce33 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -41,6 +41,7 @@ struct max98088_priv {
enum max98088_type devtype;
struct max98088_pdata *pdata;
struct clk *mclk;
+ unsigned char mclk_prescaler;
unsigned int sysclk;
struct max98088_cdata dai[2];
int eq_textcnt;
@@ -998,13 +999,16 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
/* Configure NI when operating as master */
if (snd_soc_component_read(component, M98088_REG_14_DAI1_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
@@ -1065,13 +1069,16 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
/* Configure NI when operating as master */
if (snd_soc_component_read(component, M98088_REG_1C_DAI2_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
dev_err(component->dev, "Invalid system clock frequency\n");
return -EINVAL;
}
ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)rate;
- do_div(ni, (unsigned long long int)max98088->sysclk);
+ pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler);
+ ni = DIV_ROUND_CLOSEST_ULL(ni, pclk);
snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
@@ -1113,8 +1120,10 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
*/
if ((freq >= 10000000) && (freq < 20000000)) {
snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x10);
+ max98088->mclk_prescaler = 1;
} else if ((freq >= 20000000) && (freq < 30000000)) {
snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x20);
+ max98088->mclk_prescaler = 2;
} else {
dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
index 4222aed013f1..78314187d37e 100644
--- a/sound/soc/codecs/mt6359-accdet.c
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -414,7 +414,7 @@ static void mt6359_accdet_work(struct work_struct *work)
static void mt6359_accdet_jd_work(struct work_struct *work)
{
- int ret = 0;
+ int ret;
unsigned int value = 0;
struct mt6359_accdet *priv =
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index b909b36582b7..2d6a4a29b850 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -271,7 +271,7 @@ static void hp_aux_feedback_loop_gain_ramp(struct mt6359_priv *priv, bool up)
static void hp_in_pair_current(struct mt6359_priv *priv, bool increase)
{
- int i = 0, stage = 0;
+ int i, stage;
int target = 0x3;
/* Set input diff pair bias select (Hi-Fi mode) */
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 821e7395f90f..b6fd412441a1 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -573,6 +573,30 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static u64 pcm3168a_dai_formats[] = {
+ /*
+ * Select below from Sound Card, not here
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+
+ /*
+ * First Priority
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
+ /*
+ * Second Priority
+ *
+ * These have picky limitation.
+ * see
+ * pcm3168a_hw_params()
+ */
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
.set_fmt = pcm3168a_set_dai_fmt,
.set_sysclk = pcm3168a_set_dai_sysclk,
@@ -580,6 +604,8 @@ static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
.mute_stream = pcm3168a_mute,
.set_tdm_slot = pcm3168a_set_tdm_slot,
.no_capture_mute = 1,
+ .auto_selectable_formats = pcm3168a_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(pcm3168a_dai_formats),
};
static struct snd_soc_dai_driver pcm3168a_dais[] = {
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
index bfefefcc76d8..758d439e8c7a 100644
--- a/sound/soc/codecs/rk3328_codec.c
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -474,7 +474,8 @@ static int rk3328_platform_probe(struct platform_device *pdev)
rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(rk3328->pclk)) {
dev_err(&pdev->dev, "can't get acodec pclk\n");
- return PTR_ERR(rk3328->pclk);
+ ret = PTR_ERR(rk3328->pclk);
+ goto err_unprepare_mclk;
}
ret = clk_prepare_enable(rk3328->pclk);
@@ -484,19 +485,34 @@ static int rk3328_platform_probe(struct platform_device *pdev)
}
base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err_unprepare_pclk;
+ }
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&rk3328_codec_regmap_config);
- if (IS_ERR(rk3328->regmap))
- return PTR_ERR(rk3328->regmap);
+ if (IS_ERR(rk3328->regmap)) {
+ ret = PTR_ERR(rk3328->regmap);
+ goto err_unprepare_pclk;
+ }
platform_set_drvdata(pdev, rk3328);
- return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
rk3328_dai,
ARRAY_SIZE(rk3328_dai));
+ if (ret)
+ goto err_unprepare_pclk;
+
+ return 0;
+
+err_unprepare_pclk:
+ clk_disable_unprepare(rk3328->pclk);
+
+err_unprepare_mclk:
+ clk_disable_unprepare(rk3328->mclk);
+ return ret;
}
static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = {
diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c
new file mode 100644
index 000000000000..d3a19fc9b5d0
--- /dev/null
+++ b/sound/soc/codecs/rk817_codec.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk817 ALSA SoC Audio driver
+//
+// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mfd/rk808.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct rk817_codec_priv {
+ struct snd_soc_component *component;
+ struct rk808 *rk808;
+ struct clk *mclk;
+ unsigned int stereo_sysclk;
+ bool mic_in_differential;
+};
+
+/*
+ * This sets the codec up with the values defined in the default implementation including the APLL
+ * from the Rockchip vendor kernel. I do not know if these values are universal despite differing
+ * from the default values defined above and taken from the datasheet, or implementation specific.
+ * I don't have another implementation to compare from the Rockchip sources. Hard-coding for now.
+ * Additionally, I do not know according to the documentation the units accepted for the clock
+ * values, so for the moment those are left unvalidated.
+ */
+
+static int rk817_init(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_write(component, RK817_CODEC_DDAC_POPD_DACST, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DDAC_SR_LMT0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DADC_SR_ACL0, 0x02);
+ snd_soc_component_write(component, RK817_CODEC_DTOP_VUCTIME, 0xf4);
+ if (rk817->mic_in_differential) {
+ snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK,
+ MIC_DIFF_EN);
+ }
+
+ return 0;
+}
+
+static int rk817_set_component_pll(struct snd_soc_component *component,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ /* Set resistor value and charge pump current for PLL. */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG1, 0x58);
+ /* Set the PLL feedback clock divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG2, 0x2d);
+ /* Set the PLL pre-divide value (values not documented). */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, 0x0c);
+ /* Set the PLL VCO output clock divide and PLL divided ratio of PLL High Clk (values not
+ * documented).
+ */
+ snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5);
+
+ return 0;
+}
+
+/*
+ * DDAC/DADC L/R volume setting
+ * 0db~-95db, 0.375db/step, for example:
+ * 0x00: 0dB
+ * 0xff: -95dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_vol_tlv, -9500, 0);
+
+/*
+ * PGA GAIN L/R volume setting
+ * 27db~-18db, 3db/step, for example:
+ * 0x0: -18dB
+ * 0xf: 27dB
+ */
+
+static const DECLARE_TLV_DB_MINMAX(rk817_gain_tlv, -1800, 2700);
+
+static const struct snd_kcontrol_new rk817_volume_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", RK817_CODEC_DDAC_VOLL,
+ RK817_CODEC_DDAC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", RK817_CODEC_DADC_VOLL,
+ RK817_CODEC_DADC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv),
+ SOC_DOUBLE_TLV("Mic Capture Gain", RK817_CODEC_DMIC_PGA_GAIN, 4, 0, 0xf, 0,
+ rk817_gain_tlv),
+};
+
+/* Since the speaker output and L headphone pin are internally the same, make audio path mutually
+ * exclusive with a mux.
+ */
+
+static const char *dac_mux_text[] = {
+ "HP",
+ "SPK",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(dac_enum, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+ SOC_DAPM_ENUM("Playback Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget rk817_dapm_widgets[] = {
+
+ /* capture/playback common */
+ SND_SOC_DAPM_SUPPLY("LDO Regulator", RK817_CODEC_AREF_RTCFG1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IBIAS Block", RK817_CODEC_AREF_RTCFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VAvg Buffer", RK817_CODEC_AREF_RTCFG1, 1, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL Power", RK817_CODEC_APLL_CFG5, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX1 Transfer Start", RK817_CODEC_DI2S_RXCMD_TSD, 5, 0, NULL, 0),
+
+ /* capture path common */
+ SND_SOC_DAPM_SUPPLY("ADC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC Power On", RK817_CODEC_AMIC_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Transfer Start", RK817_CODEC_DI2S_TXCR3_TXCMD, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S TX3 Right Justified", RK817_CODEC_DI2S_TXCR3_TXCMD, 3, 0, NULL, 0),
+
+ /* capture path L */
+ SND_SOC_DAPM_ADC("ADC L", "Capture", RK817_CODEC_AADC_CFG0, 7, 1),
+ SND_SOC_DAPM_SUPPLY("PGA L Power On", RK817_CODEC_AMIC_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost L2", RK817_CODEC_AMIC_CFG0, 2, 0, NULL, 0),
+
+ /* capture path R */
+ SND_SOC_DAPM_ADC("ADC R", "Capture", RK817_CODEC_AADC_CFG0, 6, 1),
+ SND_SOC_DAPM_SUPPLY("PGA R Power On", RK817_CODEC_AMIC_CFG0, 4, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Boost R2", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0),
+
+ /* playback path common */
+ SND_SOC_DAPM_SUPPLY("DAC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("I2S RX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Bias", RK817_CODEC_ADAC_CFG1, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Mute Off", RK817_CODEC_DDAC_MUTE_MIXCTL, 0, 1, NULL, 0),
+
+ /* playback path speaker */
+ SND_SOC_DAPM_SUPPLY("Class D Mode", RK817_CODEC_DDAC_MUTE_MIXCTL, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("High Pass Filter", RK817_CODEC_DDAC_MUTE_MIXCTL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("SPK DAC", "Playback", RK817_CODEC_ADAC_CFG1, 2, 1),
+ SND_SOC_DAPM_SUPPLY("Enable Class D", RK817_CODEC_ACLASSD_CFG1, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Disable Class D Mute Ramp", RK817_CODEC_ACLASSD_CFG1, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 1", RK817_CODEC_ACLASSD_CFG1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D Mute Rate 2", RK817_CODEC_ACLASSD_CFG1, 2, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 2", RK817_CODEC_ACLASSD_CFG2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPP 3", RK817_CODEC_ACLASSD_CFG2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 2", RK817_CODEC_ACLASSD_CFG2, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Class D OCPN 3", RK817_CODEC_ACLASSD_CFG2, 0, 0, NULL, 0),
+
+ /* playback path headphones */
+ SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", RK817_CODEC_AHP_CP, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone CP Discharge LDO", RK817_CODEC_AHP_CP, 3, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone OStage", RK817_CODEC_AHP_CFG0, 6, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Headphone Pre Amp", RK817_CODEC_AHP_CFG0, 5, 1, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC L", "Playback", RK817_CODEC_ADAC_CFG1, 1, 1),
+ SND_SOC_DAPM_DAC("DAC R", "Playback", RK817_CODEC_ADAC_CFG1, 0, 1),
+
+ /* Mux for input/output path selection */
+ SND_SOC_DAPM_MUX("Playback Mux", SND_SOC_NOPM, 1, 0, &dac_mux),
+
+ /* Pins for Simple Card Bindings */
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+ SND_SOC_DAPM_OUTPUT("SPKO"),
+};
+
+static const struct snd_soc_dapm_route rk817_dapm_routes[] = {
+
+ /* capture path */
+ /* left mic */
+ {"ADC L", NULL, "LDO Regulator"},
+ {"ADC L", NULL, "IBIAS Block"},
+ {"ADC L", NULL, "VAvg Buffer"},
+ {"ADC L", NULL, "PLL Power"},
+ {"ADC L", NULL, "ADC Clock"},
+ {"ADC L", NULL, "I2S TX Clock"},
+ {"ADC L", NULL, "ADC Channel Enable"},
+ {"ADC L", NULL, "I2S TX Channel Enable"},
+ {"ADC L", NULL, "I2S TX1 Transfer Start"},
+ {"MICL", NULL, "MIC Power On"},
+ {"MICL", NULL, "PGA L Power On"},
+ {"MICL", NULL, "Mic Boost L1"},
+ {"MICL", NULL, "Mic Boost L2"},
+ {"MICL", NULL, "I2S TX3 Transfer Start"},
+ {"MICL", NULL, "I2S TX3 Right Justified"},
+ {"ADC L", NULL, "MICL"},
+
+ /* right mic */
+ {"ADC R", NULL, "LDO Regulator"},
+ {"ADC R", NULL, "IBIAS Block"},
+ {"ADC R", NULL, "VAvg Buffer"},
+ {"ADC R", NULL, "PLL Power"},
+ {"ADC R", NULL, "ADC Clock"},
+ {"ADC R", NULL, "I2S TX Clock"},
+ {"ADC R", NULL, "ADC Channel Enable"},
+ {"ADC R", NULL, "I2S TX Channel Enable"},
+ {"ADC R", NULL, "I2S TX1 Transfer Start"},
+ {"MICR", NULL, "MIC Power On"},
+ {"MICR", NULL, "PGA R Power On"},
+ {"MICR", NULL, "Mic Boost R1"},
+ {"MICR", NULL, "Mic Boost R2"},
+ {"MICR", NULL, "I2S TX3 Transfer Start"},
+ {"MICR", NULL, "I2S TX3 Right Justified"},
+ {"ADC R", NULL, "MICR"},
+
+ /* playback path */
+ /* speaker path */
+ {"SPK DAC", NULL, "LDO Regulator"},
+ {"SPK DAC", NULL, "IBIAS Block"},
+ {"SPK DAC", NULL, "VAvg Buffer"},
+ {"SPK DAC", NULL, "PLL Power"},
+ {"SPK DAC", NULL, "I2S TX1 Transfer Start"},
+ {"SPK DAC", NULL, "DAC Clock"},
+ {"SPK DAC", NULL, "I2S RX Clock"},
+ {"SPK DAC", NULL, "DAC Channel Enable"},
+ {"SPK DAC", NULL, "I2S RX Channel Enable"},
+ {"SPK DAC", NULL, "Class D Mode"},
+ {"SPK DAC", NULL, "DAC Bias"},
+ {"SPK DAC", NULL, "DAC Mute Off"},
+ {"SPK DAC", NULL, "Enable Class D"},
+ {"SPK DAC", NULL, "Disable Class D Mute Ramp"},
+ {"SPK DAC", NULL, "Class D Mute Rate 1"},
+ {"SPK DAC", NULL, "Class D Mute Rate 2"},
+ {"SPK DAC", NULL, "Class D OCPP 2"},
+ {"SPK DAC", NULL, "Class D OCPP 3"},
+ {"SPK DAC", NULL, "Class D OCPN 2"},
+ {"SPK DAC", NULL, "Class D OCPN 3"},
+ {"SPK DAC", NULL, "High Pass Filter"},
+
+ /* headphone path L */
+ {"DAC L", NULL, "LDO Regulator"},
+ {"DAC L", NULL, "IBIAS Block"},
+ {"DAC L", NULL, "VAvg Buffer"},
+ {"DAC L", NULL, "PLL Power"},
+ {"DAC L", NULL, "I2S TX1 Transfer Start"},
+ {"DAC L", NULL, "DAC Clock"},
+ {"DAC L", NULL, "I2S RX Clock"},
+ {"DAC L", NULL, "DAC Channel Enable"},
+ {"DAC L", NULL, "I2S RX Channel Enable"},
+ {"DAC L", NULL, "DAC Bias"},
+ {"DAC L", NULL, "DAC Mute Off"},
+ {"DAC L", NULL, "Headphone Charge Pump"},
+ {"DAC L", NULL, "Headphone CP Discharge LDO"},
+ {"DAC L", NULL, "Headphone OStage"},
+ {"DAC L", NULL, "Headphone Pre Amp"},
+
+ /* headphone path R */
+ {"DAC R", NULL, "LDO Regulator"},
+ {"DAC R", NULL, "IBIAS Block"},
+ {"DAC R", NULL, "VAvg Buffer"},
+ {"DAC R", NULL, "PLL Power"},
+ {"DAC R", NULL, "I2S TX1 Transfer Start"},
+ {"DAC R", NULL, "DAC Clock"},
+ {"DAC R", NULL, "I2S RX Clock"},
+ {"DAC R", NULL, "DAC Channel Enable"},
+ {"DAC R", NULL, "I2S RX Channel Enable"},
+ {"DAC R", NULL, "DAC Bias"},
+ {"DAC R", NULL, "DAC Mute Off"},
+ {"DAC R", NULL, "Headphone Charge Pump"},
+ {"DAC R", NULL, "Headphone CP Discharge LDO"},
+ {"DAC R", NULL, "Headphone OStage"},
+ {"DAC R", NULL, "Headphone Pre Amp"},
+
+ /* mux path for output selection */
+ {"Playback Mux", "HP", "DAC L"},
+ {"Playback Mux", "HP", "DAC R"},
+ {"Playback Mux", "SPK", "SPK DAC"},
+ {"SPKO", NULL, "Playback Mux"},
+ {"HPOL", NULL, "Playback Mux"},
+ {"HPOR", NULL, "Playback Mux"},
+};
+
+static int rk817_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+
+ rk817->stereo_sysclk = freq;
+
+ return 0;
+}
+
+static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ unsigned int i2s_mst = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s_mst |= RK817_I2S_MODE_SLV;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s_mst |= RK817_I2S_MODE_MST;
+ break;
+ default:
+ dev_err(component->dev, "%s : set master mask failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM,
+ RK817_I2S_MODE_MASK, i2s_mst);
+
+ return 0;
+}
+
+static int rk817_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_16BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_16BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2,
+ VDW_RX_24BITS);
+ snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2,
+ VDW_TX_24BITS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rk817_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+
+ if (mute)
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_ENABLE);
+ else
+ snd_soc_component_update_bits(component,
+ RK817_CODEC_DDAC_MUTE_MIXCTL,
+ DACMT_MASK, DACMT_DISABLE);
+
+ return 0;
+}
+
+#define RK817_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define RK817_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops rk817_dai_ops = {
+ .hw_params = rk817_hw_params,
+ .set_fmt = rk817_set_dai_fmt,
+ .set_sysclk = rk817_set_dai_sysclk,
+ .mute_stream = rk817_digital_mute,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver rk817_dai[] = {
+ {
+ .name = "rk817-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = RK817_PLAYBACK_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RK817_CAPTURE_RATES,
+ .formats = RK817_FORMATS,
+ },
+ .ops = &rk817_dai_ops,
+ },
+};
+
+static int rk817_probe(struct snd_soc_component *component)
+{
+ struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
+ struct rk808 *rk808 = dev_get_drvdata(component->dev->parent);
+
+ snd_soc_component_init_regmap(component, rk808->regmap);
+ rk817->component = component;
+
+ snd_soc_component_write(component, RK817_CODEC_DTOP_LPT_SRST, 0x40);
+
+ rk817_init(component);
+
+ /* setting initial pll values so that we can continue to leverage simple-audio-card.
+ * The values aren't important since no parameters are used.
+ */
+
+ snd_soc_component_set_pll(component, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static void rk817_remove(struct snd_soc_component *component)
+{
+ snd_soc_component_exit_regmap(component);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rk817 = {
+ .probe = rk817_probe,
+ .remove = rk817_remove,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+ .controls = rk817_volume_controls,
+ .num_controls = ARRAY_SIZE(rk817_volume_controls),
+ .dapm_routes = rk817_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk817_dapm_routes),
+ .dapm_widgets = rk817_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk817_dapm_widgets),
+ .set_pll = rk817_set_component_pll,
+};
+
+static void rk817_codec_parse_dt_property(struct device *dev,
+ struct rk817_codec_priv *rk817)
+{
+ struct device_node *node;
+
+ node = of_get_child_by_name(dev->parent->of_node, "codec");
+ if (!node) {
+ dev_dbg(dev, "%s() Can not get child: codec\n",
+ __func__);
+ }
+
+ rk817->mic_in_differential =
+ of_property_read_bool(node, "rockchip,mic-in-differential");
+
+ of_node_put(node);
+}
+
+static int rk817_platform_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk817_codec_priv *rk817_codec_data;
+ int ret;
+
+ rk817_codec_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct rk817_codec_priv),
+ GFP_KERNEL);
+ if (!rk817_codec_data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rk817_codec_data);
+
+ rk817_codec_data->rk808 = rk808;
+
+ rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data);
+
+ rk817_codec_data->mclk = clk_get(pdev->dev.parent, "mclk");
+ if (IS_ERR(rk817_codec_data->mclk)) {
+ dev_dbg(&pdev->dev, "Unable to get mclk\n");
+ ret = -ENXIO;
+ goto err_;
+ }
+
+ ret = clk_prepare_enable(rk817_codec_data->mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() clock prepare error %d\n",
+ __func__, ret);
+ goto err_;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817,
+ rk817_dai, ARRAY_SIZE(rk817_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s() register codec error %d\n",
+ __func__, ret);
+ goto err_;
+ }
+
+ return 0;
+err_:
+
+ return ret;
+}
+
+static int rk817_platform_remove(struct platform_device *pdev)
+{
+ struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(rk817->mclk);
+
+ return 0;
+}
+
+static struct platform_driver rk817_codec_driver = {
+ .driver = {
+ .name = "rk817-codec",
+ },
+ .probe = rk817_platform_probe,
+ .remove = rk817_platform_remove,
+};
+
+module_platform_driver(rk817_codec_driver);
+
+MODULE_DESCRIPTION("ASoC RK817 codec driver");
+MODULE_AUTHOR("binyuan <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index 10656a5927f1..8c0b00242bb8 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -372,8 +372,8 @@ static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
RT1019_AUTO_BITS_SEL_MANU | RT1019_AUTO_CLK_SEL_MANU);
snd_soc_component_update_bits(component, RT1019_PLL_1,
RT1019_PLL_M_MASK | RT1019_PLL_M_BP_MASK | RT1019_PLL_Q_8_8_MASK,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT1019_PLL_M_SFT |
- pll_code.m_bp << RT1019_PLL_M_BP_SFT |
+ ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1019_PLL_M_SFT) |
+ (pll_code.m_bp << RT1019_PLL_M_BP_SFT) |
((pll_code.n_code >> 8) & RT1019_PLL_Q_8_8_MASK));
snd_soc_component_update_bits(component, RT1019_PLL_2,
RT1019_PLL_Q_7_0_MASK, pll_code.n_code & RT1019_PLL_Q_7_0_MASK);
@@ -522,6 +522,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1019 = {
.num_dapm_widgets = ARRAY_SIZE(rt1019_dapm_widgets),
.dapm_routes = rt1019_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes),
+ .non_legacy_dai_naming = 1,
};
static const struct regmap_config rt1019_regmap = {
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 802f4851c3df..caa720884e3e 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -725,7 +725,6 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- d_len_code = 0;
switch (params_width(params)) {
/* bit 6:4 Bits per Sample */
case 16:
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index 8ea9f1d9fec0..4a56a52adab5 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -273,12 +273,23 @@ static void rt5682_i2c_shutdown(struct i2c_client *client)
{
struct rt5682_priv *rt5682 = i2c_get_clientdata(client);
+ disable_irq(client->irq);
cancel_delayed_work_sync(&rt5682->jack_detect_work);
cancel_delayed_work_sync(&rt5682->jd_check_work);
rt5682_reset(rt5682);
}
+static int rt5682_i2c_remove(struct i2c_client *client)
+{
+ struct rt5682_priv *rt5682 = i2c_get_clientdata(client);
+
+ rt5682_i2c_shutdown(client);
+ regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies);
+
+ return 0;
+}
+
static const struct of_device_id rt5682_of_match[] = {
{.compatible = "realtek,rt5682i"},
{},
@@ -305,6 +316,7 @@ static struct i2c_driver rt5682_i2c_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = rt5682_i2c_probe,
+ .remove = rt5682_i2c_remove,
.shutdown = rt5682_i2c_shutdown,
.id_table = rt5682_i2c_id,
};
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index cc36739f7fcf..24a084e0b48a 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -683,13 +683,13 @@ static int rt711_sdca_set_fu1e_capture_ctl(struct rt711_sdca_priv *rt711)
ch_r = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_r_mute) ? 0x01 : 0x00;
err = regmap_write(rt711->regmap,
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU1E,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l);
if (err < 0)
return err;
err = regmap_write(rt711->regmap,
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU1E,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r);
if (err < 0)
return err;
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index d63b8c366efb..2783eff633a1 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -44,7 +44,6 @@ struct sigmadsp {
struct sigmadsp *devm_sigmadsp_init(struct device *dev,
const struct sigmadsp_ops *ops, const char *firmware_name);
-void sigmadsp_reset(struct sigmadsp *sigmadsp);
int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
struct snd_pcm_substream *substream);
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index ffdf7e559515..82a24e330065 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -408,6 +408,7 @@ static const struct of_device_id sti_sas_dev_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, sti_sas_dev_match);
static int sti_sas_driver_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c
new file mode 100644
index 000000000000..643b45188b6f
--- /dev/null
+++ b/sound/soc/codecs/tfa989x.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Stephan Gerhold
+ *
+ * Register definitions/sequences taken from various tfa98xx kernel drivers:
+ * Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
+ * Copyright (C) 2013 Sony Mobile Communications Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define TFA989X_STATUSREG 0x00
+#define TFA989X_BATTERYVOLTAGE 0x01
+#define TFA989X_TEMPERATURE 0x02
+#define TFA989X_REVISIONNUMBER 0x03
+#define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */
+#define TFA989X_I2SREG 0x04
+#define TFA989X_I2SREG_CHSA 6 /* amplifier input select */
+#define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6)
+#define TFA989X_I2SREG_I2SSR 12 /* sample rate */
+#define TFA989X_I2SREG_I2SSR_MSK GENMASK(15, 12)
+#define TFA989X_BAT_PROT 0x05
+#define TFA989X_AUDIO_CTR 0x06
+#define TFA989X_DCDCBOOST 0x07
+#define TFA989X_SPKR_CALIBRATION 0x08
+#define TFA989X_SYS_CTRL 0x09
+#define TFA989X_SYS_CTRL_PWDN 0 /* power down */
+#define TFA989X_SYS_CTRL_I2CR 1 /* I2C reset */
+#define TFA989X_SYS_CTRL_CFE 2 /* enable CoolFlux DSP */
+#define TFA989X_SYS_CTRL_AMPE 3 /* enable amplifier */
+#define TFA989X_SYS_CTRL_DCA 4 /* enable boost */
+#define TFA989X_SYS_CTRL_SBSL 5 /* DSP configured */
+#define TFA989X_SYS_CTRL_AMPC 6 /* amplifier enabled by DSP */
+#define TFA989X_I2S_SEL_REG 0x0a
+#define TFA989X_I2S_SEL_REG_SPKR_MSK GENMASK(10, 9) /* speaker impedance */
+#define TFA989X_I2S_SEL_REG_DCFG_MSK GENMASK(14, 11) /* DCDC compensation */
+#define TFA989X_PWM_CONTROL 0x41
+#define TFA989X_CURRENTSENSE1 0x46
+#define TFA989X_CURRENTSENSE2 0x47
+#define TFA989X_CURRENTSENSE3 0x48
+#define TFA989X_CURRENTSENSE4 0x49
+
+#define TFA9895_REVISION 0x12
+#define TFA9897_REVISION 0x97
+
+struct tfa989x_rev {
+ unsigned int rev;
+ int (*init)(struct regmap *regmap);
+};
+
+struct tfa989x {
+ struct regulator *vddd_supply;
+};
+
+static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg > TFA989X_REVISIONNUMBER;
+}
+
+static bool tfa989x_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg < TFA989X_REVISIONNUMBER;
+}
+
+static const struct regmap_config tfa989x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .writeable_reg = tfa989x_writeable_reg,
+ .volatile_reg = tfa989x_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ };
+static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text);
+static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum);
+
+static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux),
+ SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = {
+ {"OUT", NULL, "AMPE"},
+ {"AMPE", NULL, "POWER"},
+ {"AMPE", NULL, "Amp Input"},
+ {"Amp Input", "Left", "AIFINL"},
+ {"Amp Input", "Right", "AIFINR"},
+};
+
+static const struct snd_soc_component_driver tfa989x_component = {
+ .dapm_widgets = tfa989x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets),
+ .dapm_routes = tfa989x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa989x_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static const unsigned int tfa989x_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static int tfa989x_find_sample_rate(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tfa989x_rates); ++i)
+ if (tfa989x_rates[i] == rate)
+ return i;
+
+ return -EINVAL;
+}
+
+static int tfa989x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int sr;
+
+ sr = tfa989x_find_sample_rate(params_rate(params));
+ if (sr < 0)
+ return sr;
+
+ return snd_soc_component_update_bits(component, TFA989X_I2SREG,
+ TFA989X_I2SREG_I2SSR_MSK,
+ sr << TFA989X_I2SREG_I2SSR);
+}
+
+static const struct snd_soc_dai_ops tfa989x_dai_ops = {
+ .hw_params = tfa989x_hw_params,
+};
+
+static struct snd_soc_dai_driver tfa989x_dai = {
+ .name = "tfa989x-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tfa989x_dai_ops,
+};
+
+static const struct reg_sequence tfa9895_reg_init[] = {
+ /* some other registers must be set for optimal amplifier behaviour */
+ { TFA989X_BAT_PROT, 0x13ab },
+ { TFA989X_AUDIO_CTR, 0x001f },
+
+ /* peak voltage protection is always on, but may be written */
+ { TFA989X_SPKR_CALIBRATION, 0x3c4e },
+
+ /* TFA989X_SYSCTRL_DCA = 0 */
+ { TFA989X_SYS_CTRL, 0x024d },
+ { TFA989X_PWM_CONTROL, 0x0308 },
+ { TFA989X_CURRENTSENSE4, 0x0e82 },
+};
+
+static int tfa9895_init(struct regmap *regmap)
+{
+ return regmap_multi_reg_write(regmap, tfa9895_reg_init,
+ ARRAY_SIZE(tfa9895_reg_init));
+}
+
+static const struct tfa989x_rev tfa9895_rev = {
+ .rev = TFA9895_REVISION,
+ .init = tfa9895_init,
+};
+
+static int tfa9897_init(struct regmap *regmap)
+{
+ int ret;
+
+ /* Reduce slewrate by clearing iddqtestbst to avoid booster damage */
+ ret = regmap_write(regmap, TFA989X_CURRENTSENSE3, 0x0300);
+ if (ret)
+ return ret;
+
+ /* Enable clipping */
+ ret = regmap_clear_bits(regmap, TFA989X_CURRENTSENSE4, 0x1);
+ if (ret)
+ return ret;
+
+ /* Set required TDM configuration */
+ return regmap_write(regmap, 0x14, 0x0);
+}
+
+static const struct tfa989x_rev tfa9897_rev = {
+ .rev = TFA9897_REVISION,
+ .init = tfa9897_init,
+};
+
+/*
+ * Note: At the moment this driver bypasses the "CoolFlux DSP" built into the
+ * TFA989X amplifiers. Unfortunately, there seems to be absolutely
+ * no documentation for it - the public "short datasheets" do not provide
+ * any information about the DSP or available registers.
+ *
+ * Usually the TFA989X amplifiers are configured through proprietary userspace
+ * libraries. There are also some (rather complex) kernel drivers but even those
+ * rely on obscure firmware blobs for configuration (so-called "containers").
+ * They seem to contain different "profiles" with tuned speaker settings, sample
+ * rates and volume steps (which would be better exposed as separate ALSA mixers).
+ *
+ * Bypassing the DSP disables volume control (and perhaps some speaker
+ * optimization?), but at least allows using the speaker without obscure
+ * kernel drivers and firmware.
+ *
+ * Ideally NXP (or now Goodix) should release proper documentation for these
+ * amplifiers so that support for the "CoolFlux DSP" can be implemented properly.
+ */
+static int tfa989x_dsp_bypass(struct regmap *regmap)
+{
+ int ret;
+
+ /* Clear CHSA to bypass DSP and take input from I2S 1 left channel */
+ ret = regmap_clear_bits(regmap, TFA989X_I2SREG, TFA989X_I2SREG_CHSA_MSK);
+ if (ret)
+ return ret;
+
+ /* Set DCDC compensation to off and speaker impedance to 8 ohm */
+ ret = regmap_update_bits(regmap, TFA989X_I2S_SEL_REG,
+ TFA989X_I2S_SEL_REG_DCFG_MSK |
+ TFA989X_I2S_SEL_REG_SPKR_MSK,
+ TFA989X_I2S_SEL_REG_SPKR_MSK);
+ if (ret)
+ return ret;
+
+ /* Set DCDC to follower mode and disable CoolFlux DSP */
+ return regmap_clear_bits(regmap, TFA989X_SYS_CTRL,
+ BIT(TFA989X_SYS_CTRL_DCA) |
+ BIT(TFA989X_SYS_CTRL_CFE) |
+ BIT(TFA989X_SYS_CTRL_AMPC));
+}
+
+static void tfa989x_regulator_disable(void *data)
+{
+ struct tfa989x *tfa989x = data;
+
+ regulator_disable(tfa989x->vddd_supply);
+}
+
+static int tfa989x_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct tfa989x_rev *rev;
+ struct tfa989x *tfa989x;
+ struct regmap *regmap;
+ unsigned int val;
+ int ret;
+
+ rev = device_get_match_data(dev);
+ if (!rev) {
+ dev_err(dev, "unknown device revision\n");
+ return -ENODEV;
+ }
+
+ tfa989x = devm_kzalloc(dev, sizeof(*tfa989x), GFP_KERNEL);
+ if (!tfa989x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tfa989x);
+
+ tfa989x->vddd_supply = devm_regulator_get(dev, "vddd");
+ if (IS_ERR(tfa989x->vddd_supply))
+ return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply),
+ "Failed to get vddd regulator\n");
+
+ regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regulator_enable(tfa989x->vddd_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable vddd regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, tfa989x_regulator_disable, tfa989x);
+ if (ret)
+ return ret;
+
+ /* Bypass regcache for reset and init sequence */
+ regcache_cache_bypass(regmap, true);
+
+ /* Dummy read to generate i2c clocks, required on some devices */
+ regmap_read(regmap, TFA989X_REVISIONNUMBER, &val);
+
+ ret = regmap_read(regmap, TFA989X_REVISIONNUMBER, &val);
+ if (ret) {
+ dev_err(dev, "failed to read revision number: %d\n", ret);
+ return ret;
+ }
+
+ val &= TFA989X_REVISIONNUMBER_REV_MSK;
+ if (val != rev->rev) {
+ dev_err(dev, "invalid revision number, expected %#x, got %#x\n",
+ rev->rev, val);
+ return -ENODEV;
+ }
+
+ ret = regmap_write(regmap, TFA989X_SYS_CTRL, BIT(TFA989X_SYS_CTRL_I2CR));
+ if (ret) {
+ dev_err(dev, "failed to reset I2C registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = rev->init(regmap);
+ if (ret) {
+ dev_err(dev, "failed to initialize registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = tfa989x_dsp_bypass(regmap);
+ if (ret) {
+ dev_err(dev, "failed to enable DSP bypass: %d\n", ret);
+ return ret;
+ }
+ regcache_cache_bypass(regmap, false);
+
+ return devm_snd_soc_register_component(dev, &tfa989x_component,
+ &tfa989x_dai, 1);
+}
+
+static const struct of_device_id tfa989x_of_match[] = {
+ { .compatible = "nxp,tfa9895", .data = &tfa9895_rev },
+ { .compatible = "nxp,tfa9897", .data = &tfa9897_rev },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tfa989x_of_match);
+
+static struct i2c_driver tfa989x_i2c_driver = {
+ .driver = {
+ .name = "tfa989x",
+ .of_match_table = tfa989x_of_match,
+ },
+ .probe_new = tfa989x_i2c_probe,
+};
+module_i2c_driver(tfa989x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP/Goodix TFA989X (TFA1) driver");
+MODULE_AUTHOR("Stephan Gerhold <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index c7baef8948d4..077415a57225 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -261,8 +261,8 @@ static const struct snd_kcontrol_new aic26_snd_controls[] = {
* SPI device portion of driver: sysfs files for debugging
*/
-static ssize_t aic26_keyclick_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t keyclick_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct aic26 *aic26 = dev_get_drvdata(dev);
int val, amp, freq, len;
@@ -276,9 +276,9 @@ static ssize_t aic26_keyclick_show(struct device *dev,
}
/* Any write to the keyclick attribute will trigger the keyclick event */
-static ssize_t aic26_keyclick_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t keyclick_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct aic26 *aic26 = dev_get_drvdata(dev);
@@ -288,7 +288,7 @@ static ssize_t aic26_keyclick_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+static DEVICE_ATTR_RW(keyclick);
/* ---------------------------------------------------------------------
* SoC CODEC portion of driver: probe and release routines
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
new file mode 100644
index 000000000000..dee9410650d7
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -0,0 +1,1475 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "wcd-mbhc-v2.h"
+
+#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
+#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
+#define GND_MIC_SWAP_THRESHOLD 4
+#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
+#define HPHL_CROSS_CONN_THRESHOLD 100
+#define HS_VREF_MIN_VAL 1400
+#define FAKE_REM_RETRY_ATTEMPTS 3
+#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700
+#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75
+#define WCD_MBHC_ADC_MICBIAS_MV 1800
+#define WCD_MBHC_FAKE_INS_RETRY 4
+
+#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \
+ SND_JACK_MECHANICAL)
+
+#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+enum wcd_mbhc_adc_mux_ctl {
+ MUX_CTL_AUTO = 0,
+ MUX_CTL_IN2P,
+ MUX_CTL_IN3P,
+ MUX_CTL_IN4P,
+ MUX_CTL_HPH_L,
+ MUX_CTL_HPH_R,
+ MUX_CTL_NONE,
+};
+
+struct wcd_mbhc {
+ struct device *dev;
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ struct wcd_mbhc_config *cfg;
+ const struct wcd_mbhc_cb *mbhc_cb;
+ const struct wcd_mbhc_intr *intr_ids;
+ struct wcd_mbhc_field *fields;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ /* Work to correct accessory type */
+ struct work_struct correct_plug_swch;
+ struct mutex lock;
+ int buttons_pressed;
+ u32 hph_status; /* track headhpone status */
+ u8 current_plug;
+ bool is_btn_press;
+ bool in_swch_irq_handler;
+ bool hs_detect_work_stop;
+ bool is_hs_recording;
+ bool extn_cable_hph_rem;
+ bool force_linein;
+ bool impedance_detect;
+ unsigned long event_state;
+ unsigned long jiffies_atreport;
+ /* impedance of hphl and hphr */
+ uint32_t zl, zr;
+ /* Holds type of Headset - Mono/Stereo */
+ enum wcd_mbhc_hph_type hph_type;
+ /* Holds mbhc detection method - ADC/Legacy */
+ int mbhc_detection_logic;
+};
+
+static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc,
+ int field, int val)
+{
+ if (!mbhc->fields[field].reg)
+ return 0;
+
+ return snd_soc_component_write_field(mbhc->component,
+ mbhc->fields[field].reg,
+ mbhc->fields[field].mask, val);
+}
+
+static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field)
+{
+ if (!mbhc->fields[field].reg)
+ return 0;
+
+ return snd_soc_component_read_field(mbhc->component,
+ mbhc->fields[field].reg,
+ mbhc->fields[field].mask);
+}
+
+static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
+{
+ u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val);
+}
+
+static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low,
+ mbhc->cfg->btn_high,
+ mbhc->cfg->num_btn, micbias);
+}
+
+static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc,
+ const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
+{
+
+ /*
+ * Some codecs handle micbias/pullup enablement in codec
+ * drivers itself and micbias is not needed for regular
+ * plug type detection. So if micbias_control callback function
+ * is defined, just return.
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ return;
+
+ switch (cs_mb_en) {
+ case WCD_MBHC_EN_CS:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ /* Program Button threshold registers as per CS */
+ wcd_program_btn_threshold(mbhc, false);
+ break;
+ case WCD_MBHC_EN_MB:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Disable PULL_UP_EN & enable MICBIAS */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_PULLUP:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1);
+ /* Program Button threshold registers as per MICBIAS */
+ wcd_program_btn_threshold(mbhc, true);
+ break;
+ case WCD_MBHC_EN_NONE:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ break;
+ default:
+ dev_err(mbhc->dev, "%s: Invalid parameter", __func__);
+ break;
+ }
+}
+
+int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
+{
+
+ struct snd_soc_component *component;
+ bool micbias2 = false;
+
+ if (!mbhc)
+ return 0;
+
+ component = mbhc->component;
+
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2);
+
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+ mbhc->is_hs_recording = true;
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_ON:
+ /* Disable current source if micbias2 enabled */
+ if (mbhc->mbhc_cb->mbhc_micbias_control) {
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ } else {
+ mbhc->is_hs_recording = true;
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ }
+ break;
+ case WCD_EVENT_PRE_MICBIAS_2_OFF:
+ /*
+ * Before MICBIAS_2 is turned off, if FSM is enabled,
+ * make sure current source is enabled so as to detect
+ * button press/release events
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) {
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN))
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ }
+ break;
+ /* MICBIAS usage change */
+ case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+ mbhc->is_hs_recording = false;
+ break;
+ case WCD_EVENT_POST_MICBIAS_2_OFF:
+ if (!mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->is_hs_recording = false;
+
+ /* Enable PULL UP if PA's are enabled */
+ if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
+ (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state)))
+ /* enable pullup and cs, disable mb */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ else
+ /* enable current source and disable mb, pullup*/
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+
+ break;
+ case WCD_EVENT_POST_HPHL_PA_OFF:
+ clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+ break;
+ case WCD_EVENT_POST_HPHR_PA_OFF:
+ clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, pullup & enable cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS);
+ break;
+ case WCD_EVENT_PRE_HPHL_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ case WCD_EVENT_PRE_HPHR_PA_ON:
+ set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+ /* check if micbias is enabled */
+ if (micbias2)
+ /* Disable cs, pullup & enable micbias */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB);
+ else
+ /* Disable micbias, enable pullup & cs */
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify);
+
+static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
+{
+ return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+}
+
+static void wcd_micbias_disable(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
+
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false);
+
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(component);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0);
+ }
+}
+
+static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc,
+ enum snd_jack_types jack_type)
+{
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) {
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+ }
+
+ wcd_micbias_disable(mbhc);
+ mbhc->hph_type = WCD_MBHC_HPH_NONE;
+ mbhc->zl = mbhc->zr = 0;
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK);
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->force_linein = false;
+}
+
+static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc)
+{
+
+ if (!mbhc->impedance_detect)
+ return;
+
+ if (mbhc->cfg->linein_th != 0) {
+ u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
+ /* Set MUX_CTL to AUTO for Z-det */
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
+ }
+}
+
+static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc,
+ enum snd_jack_types jack_type)
+{
+ bool is_pa_on;
+ /*
+ * Report removal of current jack type.
+ * Headphone to headset shouldn't report headphone
+ * removal.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
+ jack_type == SND_JACK_HEADPHONE)
+ mbhc->hph_status &= ~SND_JACK_HEADSET;
+
+ /* Report insertion */
+ switch (jack_type) {
+ case SND_JACK_HEADPHONE:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
+ break;
+ case SND_JACK_HEADSET:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
+ mbhc->jiffies_atreport = jiffies;
+ break;
+ case SND_JACK_LINEOUT:
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ break;
+ default:
+ break;
+ }
+
+
+ is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
+
+ if (!is_pa_on) {
+ wcd_mbhc_compute_impedance(mbhc);
+ if ((mbhc->zl > mbhc->cfg->linein_th) &&
+ (mbhc->zr > mbhc->cfg->linein_th) &&
+ (jack_type == SND_JACK_HEADPHONE)) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->force_linein = true;
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ if (mbhc->hph_status) {
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT);
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ }
+ }
+
+ /* Do not calculate impedance again for lineout
+ * as during playback pa is on and impedance values
+ * will not be correct resulting in lineout detected
+ * as headphone.
+ */
+ if (is_pa_on && mbhc->force_linein) {
+ jack_type = SND_JACK_LINEOUT;
+ mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+ if (mbhc->hph_status) {
+ mbhc->hph_status &= ~(SND_JACK_HEADSET |
+ SND_JACK_LINEOUT);
+ snd_soc_jack_report(mbhc->jack, mbhc->hph_status,
+ WCD_MBHC_JACK_MASK);
+ }
+ }
+
+ mbhc->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control)
+ mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false);
+
+ snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL),
+ WCD_MBHC_JACK_MASK);
+}
+
+static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+
+ if (!insertion) /* Report removal */
+ wcd_mbhc_report_plug_removal(mbhc, jack_type);
+ else
+ wcd_mbhc_report_plug_insertion(mbhc, jack_type);
+
+}
+
+static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ mbhc->hs_detect_work_stop = true;
+ mutex_unlock(&mbhc->lock);
+ cancel_work_sync(work);
+ mutex_lock(&mbhc->lock);
+}
+
+static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc)
+{
+ /* cancel pending button press */
+ wcd_cancel_btn_work(mbhc);
+ /* cancel correct work function */
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+}
+
+static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
+{
+ wcd_mbhc_cancel_pending_work(mbhc);
+ /* Report extension cable */
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ /*
+ * Disable HPHL trigger and MIC Schmitt triggers.
+ * Setup for insertion detection.
+ */
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+ wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE);
+ /* Disable HW FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3);
+
+ /* Set the detection type appropriately */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+ enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
+}
+
+static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ if (mbhc->current_plug == plug_type)
+ return;
+
+ mutex_lock(&mbhc->lock);
+
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ break;
+ case MBHC_PLUG_TYPE_HIGH_HPH:
+ wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ break;
+ case MBHC_PLUG_TYPE_GND_MIC_SWAP:
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+ wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ break;
+ default:
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ break;
+ }
+ mutex_unlock(&mbhc->lock);
+}
+
+static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
+ struct work_struct *work)
+{
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+ mbhc->hs_detect_work_stop = false;
+ schedule_work(work);
+}
+
+static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ WARN_ON(!mutex_is_locked(&mbhc->lock));
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(component, false);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control) {
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2,
+ MICB_ENABLE);
+ wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ }
+}
+
+static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+{
+ struct snd_soc_component *component;
+ enum snd_jack_types jack_type;
+ struct wcd_mbhc *mbhc = data;
+ bool detection_type;
+
+ component = mbhc->component;
+ mutex_lock(&mbhc->lock);
+
+ mbhc->in_swch_irq_handler = true;
+
+ wcd_mbhc_cancel_pending_work(mbhc);
+
+ detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE);
+
+ /* Set the detection type appropriately */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type);
+
+ /* Enable micbias ramp */
+ if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
+ mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true);
+
+ if (detection_type) {
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
+ goto exit;
+ /* Make sure MASTER_BIAS_CTL is enabled */
+ mbhc->mbhc_cb->mbhc_bias(component, true);
+ mbhc->is_btn_press = false;
+ wcd_mbhc_adc_detect_plug_type(mbhc);
+ } else {
+ /* Disable HW FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ mbhc->extn_cable_hph_rem = false;
+
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE)
+ goto exit;
+
+ mbhc->is_btn_press = false;
+ switch (mbhc->current_plug) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ jack_type = SND_JACK_HEADSET;
+ break;
+ case MBHC_PLUG_TYPE_HIGH_HPH:
+ if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0);
+ jack_type = SND_JACK_LINEOUT;
+ break;
+ case MBHC_PLUG_TYPE_GND_MIC_SWAP:
+ dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n");
+ goto exit;
+ default:
+ dev_err(mbhc->dev, "Invalid current plug: %d\n",
+ mbhc->current_plug);
+ goto exit;
+ }
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+ wcd_mbhc_report_plug(mbhc, 0, jack_type);
+ }
+
+exit:
+ mbhc->in_swch_irq_handler = false;
+ mutex_unlock(&mbhc->lock);
+ return IRQ_HANDLED;
+}
+
+static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
+{
+ int mask = 0;
+ int btn;
+
+ btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT);
+
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ default:
+ break;
+ }
+
+ return mask;
+}
+
+static void wcd_btn_long_press_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
+
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+ snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+}
+
+static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int mask;
+ unsigned long msec_val;
+
+ mutex_lock(&mbhc->lock);
+ wcd_cancel_btn_work(mbhc);
+ mbhc->is_btn_press = true;
+ msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
+
+ /* Too short, ignore button press */
+ if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN)
+ goto done;
+
+ /* If switch interrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler)
+ goto done;
+
+ /* Plug isn't headset, ignore button press */
+ if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET)
+ goto done;
+
+ mask = wcd_mbhc_get_button_mask(mbhc);
+ mbhc->buttons_pressed |= mask;
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0)
+ WARN(1, "Button pressed twice without release event\n");
+done:
+ mutex_unlock(&mbhc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ int ret;
+
+ mutex_lock(&mbhc->lock);
+ if (mbhc->is_btn_press)
+ mbhc->is_btn_press = false;
+ else /* fake btn press */
+ goto exit;
+
+ if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK))
+ goto exit;
+
+ ret = wcd_cancel_btn_work(mbhc);
+ if (ret == 0) { /* Reporting long button release event */
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ } else {
+ if (!mbhc->in_swch_irq_handler) {
+ /* Reporting btn press n Release */
+ snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed);
+ }
+ }
+ mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+exit:
+ mutex_unlock(&mbhc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr)
+{
+
+ /* TODO Find a better way to report this to Userspace */
+ dev_err(mbhc->dev, "MBHC Over Current on %s detected\n",
+ hphr ? "HPHR" : "HPHL");
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
+{
+ return wcd_mbhc_hph_ocp_irq(data, false);
+}
+
+static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
+{
+ return wcd_mbhc_hph_ocp_irq(data, true);
+}
+
+static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_component *component = mbhc->component;
+
+ mutex_lock(&mbhc->lock);
+
+ /* enable HS detection */
+ if (mbhc->mbhc_cb->hph_pull_up_control_v2)
+ mbhc->mbhc_cb->hph_pull_up_control_v2(component,
+ HS_PULLUP_I_DEFAULT);
+ else if (mbhc->mbhc_cb->hph_pull_up_control)
+ mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT);
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
+ if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
+ mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1);
+
+ /* Insertion debounce set to 96ms */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6);
+
+ /* Button Debounce set to 16ms */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2);
+
+ /* enable bias */
+ mbhc->mbhc_cb->mbhc_bias(component, true);
+ /* enable MBHC clock */
+ if (mbhc->mbhc_cb->clk_setup)
+ mbhc->mbhc_cb->clk_setup(component, true);
+
+ /* program HS_VREF value */
+ wcd_program_hs_vref(mbhc);
+
+ wcd_program_btn_threshold(mbhc, false);
+
+ mutex_unlock(&mbhc->lock);
+
+ return 0;
+}
+
+static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc)
+{
+ int micbias = 0;
+
+ if (mbhc->mbhc_cb->get_micbias_val) {
+ mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias);
+ } else {
+ u8 vout_ctl = 0;
+ /* Read MBHC Micbias (Mic Bias2) voltage */
+ vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT);
+ /* Formula for getting micbias from vout
+ * micbias = 1.0V + VOUT_CTL * 50mV
+ */
+ micbias = 1000 + (vout_ctl * 50);
+ }
+ return micbias;
+}
+
+static int wcd_get_voltage_from_adc(u8 val, int micbias)
+{
+ /* Formula for calculating voltage from ADC
+ * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8
+ */
+ return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10));
+}
+
+static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
+{
+ u8 adc_result;
+ int output_mv;
+ int retry = 3;
+ u8 adc_en;
+
+ /* Pre-requisites for ADC continuous measurement */
+ /* Read legacy electircal detection and disable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
+ /* Set ADC to continuous measurement */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Disable ADC_ENABLE bit */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ /* Disable MBHC FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the MUX selection to IN2P */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P);
+ /* Enable MBHC FSM */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Enable ADC_ENABLE bit */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
+
+ while (retry--) {
+ /* wait for 3 msec before reading ADC result */
+ usleep_range(3000, 3100);
+ adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
+ }
+
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+ /* Get voltage from ADC result */
+ output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc));
+
+ return output_mv;
+}
+
+static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl)
+{
+ struct device *dev = mbhc->dev;
+ u8 adc_timeout = 0;
+ u8 adc_complete = 0;
+ u8 adc_result;
+ int retry = 6;
+ int ret;
+ int output_mv = 0;
+ u8 adc_en;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Trigger ADC one time measurement */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the appropriate MUX selection */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1);
+
+ while (retry--) {
+ /* wait for 600usec to get adc results */
+ usleep_range(600, 610);
+
+ /* check for ADC Timeout */
+ adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT);
+ if (adc_timeout)
+ continue;
+
+ /* Read ADC complete bit */
+ adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE);
+ if (!adc_complete)
+ continue;
+
+ /* Read ADC result */
+ adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT);
+
+ /* Get voltage from ADC result */
+ output_mv = wcd_get_voltage_from_adc(adc_result,
+ wcd_mbhc_get_micbias(mbhc));
+ break;
+ }
+
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+
+ if (retry <= 0) {
+ dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n",
+ __func__, adc_complete, adc_timeout);
+ ret = -EINVAL;
+ } else {
+ ret = output_mv;
+ }
+
+ return ret;
+}
+
+/* To determine if cross connection occurred */
+static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
+{
+ u8 adc_mode, elect_ctl, adc_en, fsm_en;
+ int hphl_adc_res, hphr_adc_res;
+ bool is_cross_conn = false;
+
+ /* If PA is enabled, dont check for cross-connection */
+ if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
+ return -EINVAL;
+
+ /* Read legacy electircal detection and disable */
+ elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+
+ /* Read and set ADC to single measurement */
+ adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE);
+ /* Read ADC Enable bit to restore after adc measurement */
+ adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN);
+ /* Read FSM status */
+ fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN);
+
+ /* Get adc result for HPH L */
+ hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L);
+ if (hphl_adc_res < 0)
+ return hphl_adc_res;
+
+ /* Get adc result for HPH R in mV */
+ hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R);
+ if (hphr_adc_res < 0)
+ return hphr_adc_res;
+
+ if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD ||
+ hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD)
+ is_cross_conn = true;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0);
+ /* Set the MUX selection to Auto */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1);
+ /* Restore ADC Enable */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en);
+ /* Restore ADC mode */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode);
+ /* Restore FSM state */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en);
+ /* Restore electrical detection */
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl);
+
+ return is_cross_conn;
+}
+
+static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc)
+{
+ int hs_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hs_thr) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hs_threshold = mbhc->cfg->hs_thr;
+ else
+ hs_threshold = (mbhc->cfg->hs_thr *
+ micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
+ micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hs_threshold;
+}
+
+static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc)
+{
+ int hph_threshold, micbias_mv;
+
+ micbias_mv = wcd_mbhc_get_micbias(mbhc);
+ if (mbhc->cfg->hph_thr) {
+ if (mbhc->cfg->micb_mv == micbias_mv)
+ hph_threshold = mbhc->cfg->hph_thr;
+ else
+ hph_threshold = (mbhc->cfg->hph_thr *
+ micbias_mv) / mbhc->cfg->micb_mv;
+ } else {
+ hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
+ micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV);
+ }
+ return hph_threshold;
+}
+
+static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc,
+ enum wcd_mbhc_plug_type plug_type)
+{
+ bool micbias2 = false;
+
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ if (mbhc->mbhc_cb->micbias_enable_status)
+ micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component,
+ MIC_BIAS_2);
+
+ if (!mbhc->is_hs_recording && !micbias2)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3);
+ break;
+ default:
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+ break;
+
+ };
+}
+
+static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable)
+{
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADSET:
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ if (mbhc->mbhc_cb->bcs_enable)
+ mbhc->mbhc_cb->bcs_enable(mbhc->component, enable);
+ break;
+ default:
+ break;
+ }
+}
+
+static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result)
+
+{
+ enum wcd_mbhc_plug_type plug_type;
+ u32 hph_thr, hs_thr;
+
+ hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc);
+ hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc);
+
+ if (adc_result < hph_thr)
+ plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+ else if (adc_result > hs_thr)
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ else
+ plug_type = MBHC_PLUG_TYPE_HEADSET;
+
+ return plug_type;
+}
+
+static void wcd_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd_mbhc *mbhc;
+ struct snd_soc_component *component;
+ enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ int pt_gnd_mic_swap_cnt = 0;
+ int output_mv, cross_conn, hs_threshold, try = 0;
+ bool is_pa_on;
+
+ mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
+ component = mbhc->component;
+
+ hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
+
+ /* Mask ADC COMPLETE interrupt */
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+
+ /* Check for cross connection */
+ do {
+ cross_conn = wcd_check_cross_conn(mbhc);
+ try++;
+ } while (try < GND_MIC_SWAP_THRESHOLD);
+
+ if (cross_conn > 0) {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ dev_err(mbhc->dev, "cross connection found, Plug type %d\n",
+ plug_type);
+ goto correct_plug_type;
+ }
+
+ /* Find plug type */
+ output_mv = wcd_measure_adc_continuous(mbhc);
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+
+ /*
+ * Report plug type if it is either headset or headphone
+ * else start the 3 sec loop
+ */
+ switch (plug_type) {
+ case MBHC_PLUG_TYPE_HEADPHONE:
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ break;
+ case MBHC_PLUG_TYPE_HEADSET:
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ break;
+ default:
+ break;
+ }
+
+correct_plug_type:
+
+ /* Disable BCS slow insertion detection */
+ wcd_mbhc_bcs_enable(mbhc, plug_type, false);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+
+ while (!time_after(jiffies, timeout)) {
+ if (mbhc->hs_detect_work_stop) {
+ wcd_micbias_disable(mbhc);
+ goto exit;
+ }
+
+ msleep(180);
+ /*
+ * Use ADC single mode to minimize the chance of missing out
+ * btn press/release for HEADSET type during correct work.
+ */
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+ is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN);
+
+ if ((output_mv <= hs_threshold) && !is_pa_on) {
+ /* Check for cross connection*/
+ cross_conn = wcd_check_cross_conn(mbhc);
+ if (cross_conn > 0) { /* cross-connection */
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD)
+ continue;
+ else
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+ } else if (!cross_conn) { /* no cross connection */
+ pt_gnd_mic_swap_cnt = 0;
+ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv);
+ continue;
+ } else if (cross_conn < 0) /* Error */
+ continue;
+
+ if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) {
+ /* US_EU gpio present, flip switch */
+ if (mbhc->cfg->swap_gnd_mic) {
+ if (mbhc->cfg->swap_gnd_mic(component, true))
+ continue;
+ }
+ }
+ }
+
+ if (output_mv > hs_threshold) /* cable is extension cable */
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+ }
+
+ wcd_mbhc_bcs_enable(mbhc, plug_type, true);
+
+ if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1);
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+
+ /*
+ * Set DETECTION_DONE bit for HEADSET
+ * so that btn press/release interrupt can be generated.
+ * For other plug type, clear the bit.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADSET)
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ else
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ wcd_mbhc_adc_update_fsm_source(mbhc, plug_type);
+
+exit:
+ if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/)
+ mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE);
+
+ /*
+ * If plug type is corrected from special headset to headphone,
+ * clear the micbias enable flag, set micbias back to 1.8V and
+ * disable micbias.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_micbias_disable(mbhc);
+ /*
+ * Enable ADC COMPLETE interrupt for HEADPHONE.
+ * Btn release may happen after the correct work, ADC COMPLETE
+ * interrupt needs to be captured to correct plug type.
+ */
+ enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr);
+ }
+
+ if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+ mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
+}
+
+static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ unsigned long timeout;
+ int adc_threshold, output_mv, retry = 0;
+ bool hphpa_on = false;
+
+ mutex_lock(&mbhc->lock);
+ timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
+ adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
+
+ do {
+ retry++;
+ /*
+ * read output_mv every 10ms to look for
+ * any change in IN2_P
+ */
+ usleep_range(10000, 10100);
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+
+ /* Check for fake removal */
+ if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS)
+ goto exit;
+ } while (!time_after(jiffies, timeout));
+
+ /*
+ * ADC COMPLETE and ELEC_REM interrupts are both enabled for
+ * HEADPHONE, need to reject the ADC COMPLETE interrupt which
+ * follows ELEC_REM one when HEADPHONE is removed.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+ mbhc->extn_cable_hph_rem = true;
+
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0);
+ wcd_mbhc_elec_hs_report_unplug(mbhc);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0);
+
+ if (hphpa_on) {
+ hphpa_on = false;
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_HPH_PA_EN, 3);
+ }
+exit:
+ mutex_unlock(&mbhc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data)
+{
+ struct wcd_mbhc *mbhc = data;
+ u8 clamp_state = 0;
+ u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY;
+
+ /*
+ * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
+ * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
+ * when HEADPHONE is removed.
+ */
+ if (mbhc->extn_cable_hph_rem == true) {
+ mbhc->extn_cable_hph_rem = false;
+ return IRQ_HANDLED;
+ }
+
+ do {
+ clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE);
+ if (clamp_state)
+ return IRQ_HANDLED;
+ /*
+ * check clamp for 120ms but at 30ms chunks to leave
+ * room for other interrupts to be processed
+ */
+ usleep_range(30000, 30100);
+ } while (--clamp_retry);
+
+ /*
+ * If current plug is headphone then there is no chance to
+ * get ADC complete interrupt, so connected cable should be
+ * headset not headphone.
+ */
+ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+ wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1);
+ wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+}
+
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr)
+{
+ *zl = mbhc->zl;
+ *zr = mbhc->zr;
+
+ if (*zl && *zr)
+ return 0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL(wcd_mbhc_get_impedance);
+
+void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
+{
+ mbhc->hph_type = hph_type;
+}
+EXPORT_SYMBOL(wcd_mbhc_set_hph_type);
+
+int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
+{
+ return mbhc->hph_type;
+}
+EXPORT_SYMBOL(wcd_mbhc_get_hph_type);
+
+int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg,
+ struct snd_soc_jack *jack)
+{
+ if (!mbhc || !cfg || !jack)
+ return -EINVAL;
+
+ mbhc->cfg = cfg;
+ mbhc->jack = jack;
+
+ return wcd_mbhc_initialise(mbhc);
+}
+EXPORT_SYMBOL(wcd_mbhc_start);
+
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+ mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+ mbhc->hph_status = 0;
+ disable_irq_nosync(mbhc->intr_ids->hph_left_ocp);
+ disable_irq_nosync(mbhc->intr_ids->hph_right_ocp);
+}
+EXPORT_SYMBOL(wcd_mbhc_stop);
+
+int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
+{
+ struct device_node *np = dev->of_node;
+ int ret, i, microvolt;
+
+ if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed"))
+ cfg->hphl_swh = false;
+ else
+ cfg->hphl_swh = true;
+
+ if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed"))
+ cfg->gnd_swh = false;
+ else
+ cfg->gnd_swh = true;
+
+ ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
+ &microvolt);
+ if (ret)
+ dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
+ else
+ cfg->hs_thr = microvolt/1000;
+
+ ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
+ &microvolt);
+ if (ret)
+ dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n");
+ else
+ cfg->hph_thr = microvolt/1000;
+
+ ret = of_property_read_u32_array(np,
+ "qcom,mbhc-buttons-vthreshold-microvolt",
+ &cfg->btn_high[0],
+ WCD_MBHC_DEF_BUTTONS);
+ if (ret)
+ dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n");
+
+ for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) {
+ if (ret) /* default voltage */
+ cfg->btn_high[i] = 500000;
+ else
+ /* Micro to Milli Volts */
+ cfg->btn_high[i] = cfg->btn_high[i]/1000;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(wcd_dt_parse_mbhc_data);
+
+struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *intr_ids,
+ struct wcd_mbhc_field *fields,
+ bool impedance_det_en)
+{
+ struct device *dev = component->dev;
+ struct wcd_mbhc *mbhc;
+ int ret;
+
+ if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) {
+ dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mbhc = devm_kzalloc(dev, sizeof(*mbhc), GFP_KERNEL);
+ if (!mbhc)
+ return ERR_PTR(-ENOMEM);
+
+ mbhc->component = component;
+ mbhc->dev = dev;
+ mbhc->intr_ids = intr_ids;
+ mbhc->mbhc_cb = mbhc_cb;
+ mbhc->fields = fields;
+ mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
+
+ if (mbhc_cb->compute_impedance)
+ mbhc->impedance_detect = impedance_det_en;
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn);
+
+ mutex_init(&mbhc->lock);
+
+ INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_sw_intr, NULL,
+ wcd_mbhc_mech_plug_detect_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "mbhc sw intr", mbhc);
+ if (ret)
+ goto err;
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_press_intr, NULL,
+ wcd_mbhc_btn_press_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Button Press detect", mbhc);
+ if (ret)
+ goto err;
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_release_intr, NULL,
+ wcd_mbhc_btn_release_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Button Release detect", mbhc);
+ if (ret)
+ goto err;
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_ins_intr, NULL,
+ wcd_mbhc_adc_hs_ins_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Elect Insert", mbhc);
+ if (ret)
+ goto err;
+
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr);
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_rem_intr, NULL,
+ wcd_mbhc_adc_hs_rem_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "Elect Remove", mbhc);
+ if (ret)
+ goto err;
+
+ disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr);
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_left_ocp, NULL,
+ wcd_mbhc_hphl_ocp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPH_L OCP detect", mbhc);
+ if (ret)
+ goto err;
+
+ ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_right_ocp, NULL,
+ wcd_mbhc_hphr_ocp_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPH_R OCP detect", mbhc);
+ if (ret)
+ goto err;
+
+ return mbhc;
+err:
+ dev_err(dev, "Failed to request mbhc interrupts %d\n", ret);
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(wcd_mbhc_init);
+
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+ mutex_lock(&mbhc->lock);
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+ mutex_unlock(&mbhc->lock);
+}
+EXPORT_SYMBOL(wcd_mbhc_deinit);
+
+static int __init mbhc_init(void)
+{
+ return 0;
+}
+
+static void __exit mbhc_exit(void)
+{
+}
+
+module_init(mbhc_init);
+module_exit(mbhc_exit);
+
+MODULE_DESCRIPTION("wcd MBHC v2 module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
new file mode 100644
index 000000000000..006118f3e81f
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __WCD_MBHC_V2_H__
+#define __WCD_MBHC_V2_H__
+
+#include <sound/jack.h>
+
+#define WCD_MBHC_FIELD(id, rreg, rmask) \
+ [id] = { .reg = rreg, .mask = rmask }
+
+enum wcd_mbhc_field_function {
+ WCD_MBHC_L_DET_EN,
+ WCD_MBHC_GND_DET_EN,
+ WCD_MBHC_MECH_DETECTION_TYPE,
+ WCD_MBHC_MIC_CLAMP_CTL,
+ WCD_MBHC_ELECT_DETECTION_TYPE,
+ WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+ WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL,
+ WCD_MBHC_HPHL_PLUG_TYPE,
+ WCD_MBHC_GND_PLUG_TYPE,
+ WCD_MBHC_SW_HPH_LP_100K_TO_GND,
+ WCD_MBHC_ELECT_SCHMT_ISRC,
+ WCD_MBHC_FSM_EN,
+ WCD_MBHC_INSREM_DBNC,
+ WCD_MBHC_BTN_DBNC,
+ WCD_MBHC_HS_VREF,
+ WCD_MBHC_HS_COMP_RESULT,
+ WCD_MBHC_IN2P_CLAMP_STATE,
+ WCD_MBHC_MIC_SCHMT_RESULT,
+ WCD_MBHC_HPHL_SCHMT_RESULT,
+ WCD_MBHC_HPHR_SCHMT_RESULT,
+ WCD_MBHC_OCP_FSM_EN,
+ WCD_MBHC_BTN_RESULT,
+ WCD_MBHC_BTN_ISRC_CTL,
+ WCD_MBHC_ELECT_RESULT,
+ WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */
+ WCD_MBHC_HPH_CNP_WG_TIME,
+ WCD_MBHC_HPHR_PA_EN,
+ WCD_MBHC_HPHL_PA_EN,
+ WCD_MBHC_HPH_PA_EN,
+ WCD_MBHC_SWCH_LEVEL_REMOVE,
+ WCD_MBHC_PULLDOWN_CTRL,
+ WCD_MBHC_ANC_DET_EN,
+ WCD_MBHC_FSM_STATUS,
+ WCD_MBHC_MUX_CTL,
+ WCD_MBHC_MOISTURE_STATUS,
+ WCD_MBHC_HPHR_GND,
+ WCD_MBHC_HPHL_GND,
+ WCD_MBHC_HPHL_OCP_DET_EN,
+ WCD_MBHC_HPHR_OCP_DET_EN,
+ WCD_MBHC_HPHL_OCP_STATUS,
+ WCD_MBHC_HPHR_OCP_STATUS,
+ WCD_MBHC_ADC_EN,
+ WCD_MBHC_ADC_COMPLETE,
+ WCD_MBHC_ADC_TIMEOUT,
+ WCD_MBHC_ADC_RESULT,
+ WCD_MBHC_MICB2_VOUT,
+ WCD_MBHC_ADC_MODE,
+ WCD_MBHC_DETECTION_DONE,
+ WCD_MBHC_ELECT_ISRC_EN,
+ WCD_MBHC_REG_FUNC_MAX,
+};
+
+#define WCD_MBHC_DEF_BUTTONS 8
+#define WCD_MBHC_KEYCODE_NUM 8
+#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100
+#define WCD_MBHC_THR_HS_MICB_MV 2700
+#define WCD_MONO_HS_MIN_THR 2
+
+enum wcd_mbhc_detect_logic {
+ WCD_DETECTION_LEGACY,
+ WCD_DETECTION_ADC,
+};
+
+enum wcd_mbhc_cs_mb_en_flag {
+ WCD_MBHC_EN_CS = 0,
+ WCD_MBHC_EN_MB,
+ WCD_MBHC_EN_PULLUP,
+ WCD_MBHC_EN_NONE,
+};
+
+enum {
+ WCD_MBHC_ELEC_HS_INS,
+ WCD_MBHC_ELEC_HS_REM,
+};
+
+enum wcd_mbhc_plug_type {
+ MBHC_PLUG_TYPE_INVALID = -1,
+ MBHC_PLUG_TYPE_NONE,
+ MBHC_PLUG_TYPE_HEADSET,
+ MBHC_PLUG_TYPE_HEADPHONE,
+ MBHC_PLUG_TYPE_HIGH_HPH,
+ MBHC_PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum pa_dac_ack_flags {
+ WCD_MBHC_HPHL_PA_OFF_ACK = 0,
+ WCD_MBHC_HPHR_PA_OFF_ACK,
+};
+
+enum wcd_mbhc_btn_det_mem {
+ WCD_MBHC_BTN_DET_V_BTN_LOW,
+ WCD_MBHC_BTN_DET_V_BTN_HIGH
+};
+
+enum {
+ MIC_BIAS_1 = 1,
+ MIC_BIAS_2,
+ MIC_BIAS_3,
+ MIC_BIAS_4
+};
+
+enum {
+ MICB_PULLUP_ENABLE,
+ MICB_PULLUP_DISABLE,
+ MICB_ENABLE,
+ MICB_DISABLE,
+};
+
+enum wcd_notify_event {
+ WCD_EVENT_INVALID,
+ /* events for micbias ON and OFF */
+ WCD_EVENT_PRE_MICBIAS_2_OFF,
+ WCD_EVENT_POST_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_MICBIAS_2_ON,
+ WCD_EVENT_POST_MICBIAS_2_ON,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF,
+ WCD_EVENT_PRE_DAPM_MICBIAS_2_ON,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON,
+ /* events for PA ON and OFF */
+ WCD_EVENT_PRE_HPHL_PA_ON,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_ON,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ WCD_EVENT_OCP_OFF,
+ WCD_EVENT_OCP_ON,
+ WCD_EVENT_LAST,
+};
+
+enum wcd_mbhc_event_state {
+ WCD_MBHC_EVENT_PA_HPHL,
+ WCD_MBHC_EVENT_PA_HPHR,
+};
+
+enum wcd_mbhc_hph_type {
+ WCD_MBHC_HPH_NONE = 0,
+ WCD_MBHC_HPH_MONO,
+ WCD_MBHC_HPH_STEREO,
+};
+
+/*
+ * These enum definitions are directly mapped to the register
+ * definitions
+ */
+
+enum mbhc_hs_pullup_iref {
+ I_DEFAULT = -1,
+ I_OFF = 0,
+ I_1P0_UA,
+ I_2P0_UA,
+ I_3P0_UA,
+};
+
+enum mbhc_hs_pullup_iref_v2 {
+ HS_PULLUP_I_DEFAULT = -1,
+ HS_PULLUP_I_3P0_UA = 0,
+ HS_PULLUP_I_2P25_UA,
+ HS_PULLUP_I_1P5_UA,
+ HS_PULLUP_I_0P75_UA,
+ HS_PULLUP_I_1P125_UA = 0x05,
+ HS_PULLUP_I_0P375_UA = 0x07,
+ HS_PULLUP_I_2P0_UA,
+ HS_PULLUP_I_1P0_UA = 0x0A,
+ HS_PULLUP_I_0P5_UA,
+ HS_PULLUP_I_0P25_UA = 0x0F,
+ HS_PULLUP_I_0P125_UA = 0x17,
+ HS_PULLUP_I_OFF,
+};
+
+enum mbhc_moisture_rref {
+ R_OFF,
+ R_24_KOHM,
+ R_84_KOHM,
+ R_184_KOHM,
+};
+
+struct wcd_mbhc_config {
+ int btn_high[WCD_MBHC_DEF_BUTTONS];
+ int btn_low[WCD_MBHC_DEF_BUTTONS];
+ int v_hs_max;
+ int num_btn;
+ bool mono_stero_detection;
+ bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active);
+ bool hs_ext_micbias;
+ bool gnd_det_en;
+ uint32_t linein_th;
+ bool moisture_en;
+ int mbhc_micbias;
+ int anc_micbias;
+ bool moisture_duty_cycle_en;
+ bool hphl_swh; /*track HPHL switch NC / NO */
+ bool gnd_swh; /*track GND switch NC / NO */
+ u32 hs_thr;
+ u32 hph_thr;
+ u32 micb_mv;
+ u32 moist_vref;
+ u32 moist_iref;
+ u32 moist_rref;
+};
+
+struct wcd_mbhc_intr {
+ int mbhc_sw_intr;
+ int mbhc_btn_press_intr;
+ int mbhc_btn_release_intr;
+ int mbhc_hs_ins_intr;
+ int mbhc_hs_rem_intr;
+ int hph_left_ocp;
+ int hph_right_ocp;
+};
+
+struct wcd_mbhc_field {
+ u16 reg;
+ u8 mask;
+};
+
+struct wcd_mbhc;
+
+struct wcd_mbhc_cb {
+ void (*update_cross_conn_thr)(struct snd_soc_component *component);
+ void (*get_micbias_val)(struct snd_soc_component *component, int *mb);
+ void (*bcs_enable)(struct snd_soc_component *component, bool bcs_enable);
+ void (*compute_impedance)(struct snd_soc_component *component,
+ uint32_t *zl, uint32_t *zr);
+ void (*set_micbias_value)(struct snd_soc_component *component);
+ void (*set_auto_zeroing)(struct snd_soc_component *component,
+ bool enable);
+ void (*clk_setup)(struct snd_soc_component *component, bool enable);
+ bool (*micbias_enable_status)(struct snd_soc_component *component, int micb_num);
+ void (*mbhc_bias)(struct snd_soc_component *component, bool enable);
+ void (*set_btn_thr)(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias);
+ void (*hph_pull_up_control)(struct snd_soc_component *component,
+ enum mbhc_hs_pullup_iref);
+ int (*mbhc_micbias_control)(struct snd_soc_component *component,
+ int micb_num, int req);
+ void (*mbhc_micb_ramp_control)(struct snd_soc_component *component,
+ bool enable);
+ bool (*extn_use_mb)(struct snd_soc_component *component);
+ int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_component *component,
+ int micb_num, bool req_en);
+ void (*mbhc_gnd_det_ctrl)(struct snd_soc_component *component,
+ bool enable);
+ void (*hph_pull_down_ctrl)(struct snd_soc_component *component,
+ bool enable);
+ void (*mbhc_moisture_config)(struct snd_soc_component *component);
+ void (*update_anc_state)(struct snd_soc_component *component,
+ bool enable, int anc_num);
+ void (*hph_pull_up_control_v2)(struct snd_soc_component *component,
+ int pull_up_cur);
+ bool (*mbhc_get_moisture_status)(struct snd_soc_component *component);
+ void (*mbhc_moisture_polling_ctrl)(struct snd_soc_component *component, bool enable);
+ void (*mbhc_moisture_detect_en)(struct snd_soc_component *component, bool enable);
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC)
+int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg);
+int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg,
+ struct snd_soc_jack *jack);
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
+void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type);
+int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc);
+struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ struct wcd_mbhc_field *fields,
+ bool impedance_det_en);
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+ uint32_t *zr);
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc);
+int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event);
+
+#else
+static inline int wcd_dt_parse_mbhc_data(struct device *dev,
+ struct wcd_mbhc_config *cfg)
+{
+ return -ENOTSUPP;
+}
+
+static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+}
+
+static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component,
+ const struct wcd_mbhc_cb *mbhc_cb,
+ const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+ struct wcd_mbhc_field *fields,
+ bool impedance_det_en)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+
+static inline void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type)
+{
+}
+
+static inline int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc)
+{
+ return -ENOTSUPP;
+}
+
+static inline int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event)
+{
+ return -ENOTSUPP;
+}
+
+static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+ struct wcd_mbhc_config *mbhc_cfg,
+ struct snd_soc_jack *jack)
+{
+ return 0;
+}
+
+static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc,
+ uint32_t *zl,
+ uint32_t *zr)
+{
+ *zl = 0;
+ *zr = 0;
+ return -EINVAL;
+}
+static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+}
+#endif
+
+#endif /* __WCD_MBHC_V2_H__ */
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 046874ef490e..c496b359f2f4 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -21,6 +21,7 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "wcd-clsh-v2.h"
+#include "wcd-mbhc-v2.h"
#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
@@ -131,6 +132,24 @@
} \
}
+/* Z value defined in milliohm */
+#define WCD934X_ZDET_VAL_32 32000
+#define WCD934X_ZDET_VAL_400 400000
+#define WCD934X_ZDET_VAL_1200 1200000
+#define WCD934X_ZDET_VAL_100K 100000000
+/* Z floating defined in ohms */
+#define WCD934X_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define WCD934X_ZDET_NUM_MEASUREMENTS 900
+#define WCD934X_MBHC_GET_C1(c) ((c & 0xC000) >> 14)
+#define WCD934X_MBHC_GET_X1(x) (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define WCD934X_MBHC_ZDET_CONST (86 * 16384)
+#define WCD934X_MBHC_MOISTURE_RREF R_24_KOHM
+#define WCD934X_MBHC_MAX_BUTTONS (8)
+#define WCD_MBHC_HS_V_MAX 1600
+
#define WCD934X_INTERPOLATOR_PATH(id) \
{"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \
{"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \
@@ -287,12 +306,7 @@
{"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \
{"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"}
-enum {
- MIC_BIAS_1 = 1,
- MIC_BIAS_2,
- MIC_BIAS_3,
- MIC_BIAS_4
-};
+#define WCD934X_MAX_MICBIAS MIC_BIAS_4
enum {
SIDO_SOURCE_INTERNAL,
@@ -486,6 +500,15 @@ static struct interp_sample_rate sr_val_tbl[] = {
{352800, 0xC},
};
+struct wcd934x_mbhc_zdet_param {
+ u16 ldo_ctl;
+ u16 noff;
+ u16 nshift;
+ u16 btn5;
+ u16 btn6;
+ u16 btn7;
+};
+
struct wcd_slim_codec_dai_data {
struct list_head slim_ch_list;
struct slim_stream_config sconfig;
@@ -541,6 +564,18 @@ struct wcd934x_codec {
int comp_enabled[COMPANDER_MAX];
int sysclk_users;
struct mutex sysclk_mutex;
+ /* mbhc module */
+ struct wcd_mbhc *mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ bool mbhc_started;
+ struct mutex micb_lock;
+ u32 micb_ref[WCD934X_MAX_MICBIAS];
+ u32 pullup_ref[WCD934X_MAX_MICBIAS];
+ u32 micb1_mv;
+ u32 micb2_mv;
+ u32 micb3_mv;
+ u32 micb4_mv;
};
#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw)
@@ -1183,6 +1218,57 @@ static const struct soc_enum cdc_if_tx13_mux_enum =
SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0,
ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text);
+static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD934X_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD934X_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD934X_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD934X_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD934X_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD934X_MBHC_NEW_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD934X_MBHC_NEW_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD934X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD934X_HPH_OCP_CTL, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD934X_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD934X_ANA_MICB2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD934X_HPH_CNP_WG_TIME, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD934X_ANA_HPH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD934X_ANA_HPH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD934X_ANA_HPH, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD934X_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD934X_MBHC_CTL_BCS, 0x02),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD934X_MBHC_STATUS_SPARE_1, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD934X_MBHC_NEW_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD934X_MBHC_NEW_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD934X_HPH_PA_CTL2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD934X_HPH_PA_CTL2, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD934X_HPH_L_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD934X_HPH_R_TEST, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD934X_MBHC_NEW_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD934X_MBHC_NEW_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD934X_MBHC_NEW_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD934X_MBHC_NEW_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD934X_ANA_MICB2, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD934X_MBHC_NEW_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD934X_MBHC_NEW_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD934X_ANA_MBHC_ZDET, 0x02),
+};
+
static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src)
{
if (sido_src == wcd->sido_input_src)
@@ -2127,7 +2213,8 @@ static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd)
return NULL;
}
-static int wcd934x_get_micbias_val(struct device *dev, const char *micbias)
+static int wcd934x_get_micbias_val(struct device *dev, const char *micbias,
+ u32 *micb_mv)
{
int mv;
@@ -2145,6 +2232,8 @@ static int wcd934x_get_micbias_val(struct device *dev, const char *micbias)
mv = WCD934X_DEF_MICBIAS_MV;
}
+ *micb_mv = mv;
+
return (mv - 1000) / 50;
}
@@ -2155,13 +2244,17 @@ static int wcd934x_init_dmic(struct snd_soc_component *comp)
u32 def_dmic_rate, dmic_clk_drv;
vout_ctl_1 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias1-microvolt");
+ "qcom,micbias1-microvolt",
+ &wcd->micb1_mv);
vout_ctl_2 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias2-microvolt");
+ "qcom,micbias2-microvolt",
+ &wcd->micb2_mv);
vout_ctl_3 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias3-microvolt");
+ "qcom,micbias3-microvolt",
+ &wcd->micb3_mv);
vout_ctl_4 = wcd934x_get_micbias_val(comp->dev,
- "qcom,micbias4-microvolt");
+ "qcom,micbias4-microvolt",
+ &wcd->micb4_mv);
snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1,
WCD934X_MICB_VAL_MASK, vout_ctl_1);
@@ -2287,6 +2380,695 @@ static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data)
return ret;
}
+static void wcd934x_mbhc_clk_setup(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_CTL_1,
+ WCD934X_MBHC_CTL_RCO_EN_MASK, enable);
+}
+
+static void wcd934x_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_ELECT,
+ WCD934X_ANA_MBHC_BIAS_EN, enable);
+}
+
+static void wcd934x_mbhc_program_btn_thr(struct snd_soc_component *component,
+ int *btn_low, int *btn_high,
+ int num_btn, bool is_micbias)
+{
+ int i, vth;
+
+ if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+ dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+ __func__, num_btn);
+ return;
+ }
+
+ for (i = 0; i < num_btn; i++) {
+ vth = ((btn_high[i] * 2) / 25) & 0x3F;
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_BTN0 + i,
+ WCD934X_MBHC_BTN_VTH_MASK, vth);
+ }
+}
+
+static bool wcd934x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num)
+{
+ u8 val;
+
+ if (micb_num == MIC_BIAS_2) {
+ val = snd_soc_component_read_field(component, WCD934X_ANA_MICB2,
+ WCD934X_ANA_MICB2_ENABLE_MASK);
+ if (val == WCD934X_MICB_ENABLE)
+ return true;
+ }
+ return false;
+}
+
+static void wcd934x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component,
+ enum mbhc_hs_pullup_iref pull_up_cur)
+{
+ /* Default pull up current to 2uA */
+ if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA ||
+ pull_up_cur == I_DEFAULT)
+ pull_up_cur = I_2P0_UA;
+
+
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_PLUG_DETECT_CTL,
+ WCD934X_HSDET_PULLUP_C_MASK, pull_up_cur);
+}
+
+static int wcd934x_micbias_control(struct snd_soc_component *component,
+ int micb_num, int req, bool is_dapm)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ }
+ mutex_lock(&wcd934x->micb_lock);
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ wcd934x->pullup_ref[micb_index]++;
+ if ((wcd934x->pullup_ref[micb_index] == 1) &&
+ (wcd934x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (wcd934x->pullup_ref[micb_index] > 0)
+ wcd934x->pullup_ref[micb_index]--;
+
+ if ((wcd934x->pullup_ref[micb_index] == 0) &&
+ (wcd934x->micb_ref[micb_index] == 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK, 0);
+ break;
+ case MICB_ENABLE:
+ wcd934x->micb_ref[micb_index]++;
+ if (wcd934x->micb_ref[micb_index] == 1) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_ENABLE);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_MICBIAS_2_ON);
+ }
+
+ if (micb_num == MIC_BIAS_2 && is_dapm)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+ break;
+ case MICB_DISABLE:
+ if (wcd934x->micb_ref[micb_index] > 0)
+ wcd934x->micb_ref[micb_index]--;
+
+ if ((wcd934x->micb_ref[micb_index] == 0) &&
+ (wcd934x->pullup_ref[micb_index] > 0))
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+ else if ((wcd934x->micb_ref[micb_index] == 0) &&
+ (wcd934x->pullup_ref[micb_index] == 0)) {
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_PRE_MICBIAS_2_OFF);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK, 0);
+ if (micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_MICBIAS_2_OFF);
+ }
+ if (is_dapm && micb_num == MIC_BIAS_2)
+ wcd_mbhc_event_notify(wcd934x->mbhc,
+ WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+ break;
+ }
+
+ mutex_unlock(&wcd934x->micb_lock);
+
+ return 0;
+}
+
+static int wcd934x_mbhc_request_micbias(struct snd_soc_component *component,
+ int micb_num, int req)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
+ int ret;
+
+ if (req == MICB_ENABLE)
+ __wcd934x_cdc_mclk_enable(wcd, true);
+
+ ret = wcd934x_micbias_control(component, micb_num, req, false);
+
+ if (req == MICB_DISABLE)
+ __wcd934x_cdc_mclk_enable(wcd, false);
+
+ return ret;
+}
+
+static void wcd934x_mbhc_micb_ramp_control(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_SHIFT_CTRL_MASK, 0x3);
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP,
+ WCD934X_RAMP_SHIFT_CTRL_MASK, 0);
+ }
+}
+
+static int wcd934x_get_micb_vout_ctl_val(u32 micb_mv)
+{
+ /* min micbias voltage is 1V and maximum is 2.85V */
+ if (micb_mv < 1000 || micb_mv > 2850)
+ return -EINVAL;
+
+ return (micb_mv - 1000) / 50;
+}
+
+static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+ int req_volt, int micb_num)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0;
+
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = WCD934X_ANA_MICB1;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = WCD934X_ANA_MICB2;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = WCD934X_ANA_MICB3;
+ break;
+ case MIC_BIAS_4:
+ micb_reg = WCD934X_ANA_MICB4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&wcd934x->micb_lock);
+ /*
+ * If requested micbias voltage is same as current micbias
+ * voltage, then just return. Otherwise, adjust voltage as
+ * per requested value. If micbias is already enabled, then
+ * to avoid slow micbias ramp-up or down enable pull-up
+ * momentarily, change the micbias value and then re-enable
+ * micbias.
+ */
+ micb_en = snd_soc_component_read_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK);
+ cur_vout_ctl = snd_soc_component_read_field(component, micb_reg,
+ WCD934X_MICB_VAL_MASK);
+
+ req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt);
+ if (req_vout_ctl < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (cur_vout_ctl == req_vout_ctl) {
+ ret = 0;
+ goto exit;
+ }
+
+ if (micb_en == WCD934X_MICB_ENABLE)
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_PULL_UP);
+
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_MICB_VAL_MASK,
+ req_vout_ctl);
+
+ if (micb_en == WCD934X_MICB_ENABLE) {
+ snd_soc_component_write_field(component, micb_reg,
+ WCD934X_ANA_MICB_EN_MASK,
+ WCD934X_MICB_ENABLE);
+ /*
+ * Add 2ms delay as per HW requirement after enabling
+ * micbias
+ */
+ usleep_range(2000, 2100);
+ }
+exit:
+ mutex_unlock(&wcd934x->micb_lock);
+ return ret;
+}
+
+static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component,
+ int micb_num, bool req_en)
+{
+ struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component);
+ int rc, micb_mv;
+
+ if (micb_num != MIC_BIAS_2)
+ return -EINVAL;
+ /*
+ * If device tree micbias level is already above the minimum
+ * voltage needed to detect threshold microphone, then do
+ * not change the micbias, just return.
+ */
+ if (wcd934x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+ return 0;
+
+ micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd934x->micb2_mv;
+
+ rc = wcd934x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+
+ return rc;
+}
+
+static inline void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x,
+ s16 *d1_a, u16 noff,
+ int32_t *zdet)
+{
+ int i;
+ int val, val1;
+ s16 c1;
+ s32 x1, d1;
+ int32_t denom;
+ int minCode_param[] = {
+ 3277, 1639, 820, 410, 205, 103, 52, 26
+ };
+
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20);
+ for (i = 0; i < WCD934X_ZDET_NUM_MEASUREMENTS; i++) {
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val);
+ if (val & 0x80)
+ break;
+ }
+ val = val << 0x8;
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1);
+ val |= val1;
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00);
+ x1 = WCD934X_MBHC_GET_X1(val);
+ c1 = WCD934X_MBHC_GET_C1(val);
+ /* If ramp is not complete, give additional 5ms */
+ if ((c1 < 2) && x1)
+ usleep_range(5000, 5050);
+
+ if (!c1 || !x1) {
+ dev_err(wcd934x->dev, "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+ __func__, c1, x1);
+ goto ramp_down;
+ }
+ d1 = d1_a[c1];
+ denom = (x1 * d1) - (1 << (14 - noff));
+ if (denom > 0)
+ *zdet = (WCD934X_MBHC_ZDET_CONST * 1000) / denom;
+ else if (x1 < minCode_param[noff])
+ *zdet = WCD934X_ZDET_FLOATING_IMPEDANCE;
+
+ dev_info(wcd934x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+ __func__, d1, c1, x1, *zdet);
+ramp_down:
+ i = 0;
+
+ while (x1) {
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val);
+ regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val1);
+ val = val << 0x08;
+ val |= val1;
+ x1 = WCD934X_MBHC_GET_X1(val);
+ i++;
+ if (i == WCD934X_ZDET_NUM_MEASUREMENTS)
+ break;
+ }
+}
+
+static void wcd934x_mbhc_zdet_ramp(struct snd_soc_component *component,
+ struct wcd934x_mbhc_zdet_param *zdet_param,
+ int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+ struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev);
+ int32_t zdet = 0;
+
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD934X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN5,
+ WCD934X_VTH_MASK, zdet_param->btn5);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN6,
+ WCD934X_VTH_MASK, zdet_param->btn6);
+ snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN7,
+ WCD934X_VTH_MASK, zdet_param->btn7);
+ snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL,
+ WCD934X_ZDET_RANGE_CTL_MASK, zdet_param->noff);
+ snd_soc_component_update_bits(component, WCD934X_MBHC_NEW_ZDET_RAMP_CTL,
+ 0x0F, zdet_param->nshift);
+
+ if (!zl)
+ goto z_right;
+ /* Start impedance measurement for HPH_L */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x80);
+ wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+ *zl = zdet;
+
+z_right:
+ if (!zr)
+ return;
+ /* Start impedance measurement for HPH_R */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x40);
+ wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet);
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+ *zr = zdet;
+}
+
+static inline void wcd934x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component,
+ int32_t *z_val, int flag_l_r)
+{
+ s16 q1;
+ int q1_cal;
+
+ if (*z_val < (WCD934X_ZDET_VAL_400/1000))
+ q1 = snd_soc_component_read(component,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+ else
+ q1 = snd_soc_component_read(component,
+ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r));
+ if (q1 & 0x80)
+ q1_cal = (10000 - ((q1 & 0x7F) * 25));
+ else
+ q1_cal = (10000 + (q1 * 25));
+ if (q1_cal > 0)
+ *z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void wcd934x_wcd_mbhc_calc_impedance(struct snd_soc_component *component,
+ uint32_t *zl, uint32_t *zr)
+{
+ struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev);
+ s16 reg0, reg1, reg2, reg3, reg4;
+ int32_t z1L, z1R, z1Ls;
+ int zMono, z_diff1, z_diff2;
+ bool is_fsm_disable = false;
+ struct wcd934x_mbhc_zdet_param zdet_param[] = {
+ {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+ {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+ {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+ {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+ };
+ struct wcd934x_mbhc_zdet_param *zdet_param_ptr = NULL;
+ s16 d1_a[][4] = {
+ {0, 30, 90, 30},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ {0, 30, 30, 5},
+ };
+ s16 *d1 = NULL;
+
+ reg0 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN5);
+ reg1 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN6);
+ reg2 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN7);
+ reg3 = snd_soc_component_read(component, WCD934X_MBHC_CTL_CLK);
+ reg4 = snd_soc_component_read(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL);
+
+ if (snd_soc_component_read(component, WCD934X_ANA_MBHC_ELECT) & 0x80) {
+ is_fsm_disable = true;
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x00);
+ }
+
+ /* For NO-jack, disable L_DET_EN before Z-det measurements */
+ if (wcd934x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x00);
+
+ /* Turn off 100k pull down on HPHL */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x00);
+
+ /* First get impedance on Left */
+ d1 = d1_a[1];
+ zdet_param_ptr = &zdet_param[1];
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+ if (!WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+ goto left_ch_impedance;
+
+ /* Second ramp for left ch */
+ if (z1L < WCD934X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1L > WCD934X_ZDET_VAL_400) &&
+ (z1L <= WCD934X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1L > WCD934X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+ if ((z1L == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1L > WCD934X_ZDET_VAL_100K)) {
+ *zl = WCD934X_ZDET_FLOATING_IMPEDANCE;
+ zdet_param_ptr = &zdet_param[1];
+ d1 = d1_a[1];
+ } else {
+ *zl = z1L/1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, zl, 0);
+ }
+ dev_info(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+ __func__, *zl);
+
+ /* Start of right impedance ramp and calculation */
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ if (WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+ if (((z1R > WCD934X_ZDET_VAL_1200) &&
+ (zdet_param_ptr->noff == 0x6)) ||
+ ((*zl) != WCD934X_ZDET_FLOATING_IMPEDANCE))
+ goto right_ch_impedance;
+ /* Second ramp for right ch */
+ if (z1R < WCD934X_ZDET_VAL_32) {
+ zdet_param_ptr = &zdet_param[0];
+ d1 = d1_a[0];
+ } else if ((z1R > WCD934X_ZDET_VAL_400) &&
+ (z1R <= WCD934X_ZDET_VAL_1200)) {
+ zdet_param_ptr = &zdet_param[2];
+ d1 = d1_a[2];
+ } else if (z1R > WCD934X_ZDET_VAL_1200) {
+ zdet_param_ptr = &zdet_param[3];
+ d1 = d1_a[3];
+ }
+ wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+ }
+right_ch_impedance:
+ if ((z1R == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (z1R > WCD934X_ZDET_VAL_100K)) {
+ *zr = WCD934X_ZDET_FLOATING_IMPEDANCE;
+ } else {
+ *zr = z1R/1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, zr, 1);
+ }
+ dev_err(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+ __func__, *zr);
+
+ /* Mono/stereo detection */
+ if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) &&
+ (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE)) {
+ dev_dbg(component->dev,
+ "%s: plug type is invalid or extension cable\n",
+ __func__);
+ goto zdet_complete;
+ }
+ if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE) ||
+ ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+ ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+ dev_dbg(component->dev,
+ "%s: Mono plug type with one ch floating or shorted to GND\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO);
+ goto zdet_complete;
+ }
+ snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST,
+ WCD934X_HPHPA_GND_OVR_MASK, 1);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, 1);
+ if (*zl < (WCD934X_ZDET_VAL_32/1000))
+ wcd934x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1);
+ else
+ wcd934x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST,
+ WCD934X_HPHPA_GND_OVR_MASK, 0);
+ z1Ls /= 1000;
+ wcd934x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0);
+ /* Parallel of left Z and 9 ohm pull down resistor */
+ zMono = ((*zl) * 9) / ((*zl) + 9);
+ z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+ z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+ if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+ dev_err(component->dev, "%s: stereo plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_STEREO);
+ } else {
+ dev_err(component->dev, "%s: MONO plug type detected\n",
+ __func__);
+ wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO);
+ }
+
+zdet_complete:
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN5, reg0);
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN6, reg1);
+ snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN7, reg2);
+ /* Turn on 100k pull down on HPHL */
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x01);
+
+ /* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+ if (wcd934x->mbhc_cfg.hphl_swh)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x80);
+
+ snd_soc_component_write(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+ snd_soc_component_write(component, WCD934X_MBHC_CTL_CLK, reg3);
+ if (is_fsm_disable)
+ regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void wcd934x_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ if (enable) {
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_HSG_PULLUP_COMP_EN, 1);
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_GND_DET_EN_MASK, 1);
+ } else {
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_GND_DET_EN_MASK, 0);
+ snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH,
+ WCD934X_MBHC_HSG_PULLUP_COMP_EN, 0);
+ }
+}
+
+static void wcd934x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+ bool enable)
+{
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_R_MASK, enable);
+ snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2,
+ WCD934X_HPHPA_GND_L_MASK, enable);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .clk_setup = wcd934x_mbhc_clk_setup,
+ .mbhc_bias = wcd934x_mbhc_mbhc_bias_control,
+ .set_btn_thr = wcd934x_mbhc_program_btn_thr,
+ .micbias_enable_status = wcd934x_mbhc_micb_en_status,
+ .hph_pull_up_control = wcd934x_mbhc_hph_l_pull_up_control,
+ .mbhc_micbias_control = wcd934x_mbhc_request_micbias,
+ .mbhc_micb_ramp_control = wcd934x_mbhc_micb_ramp_control,
+ .mbhc_micb_ctrl_thr_mic = wcd934x_mbhc_micb_ctrl_threshold_mic,
+ .compute_impedance = wcd934x_wcd_mbhc_calc_impedance,
+ .mbhc_gnd_det_ctrl = wcd934x_mbhc_gnd_det_ctrl,
+ .hph_pull_down_ctrl = wcd934x_mbhc_hph_pull_down_ctrl,
+};
+
+static int wcd934x_get_hph_type(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd->mbhc);
+
+ return 0;
+}
+
+static int wcd934x_hph_impedance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t zl, zr;
+ bool hphr;
+ struct soc_mixer_control *mc;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+ wcd_mbhc_get_impedance(wcd->mbhc, &zl, &zr);
+ dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr);
+ ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+ return 0;
+}
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+ SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+ wcd934x_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+ SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+ wcd934x_hph_impedance_get, NULL),
+ SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+ wcd934x_hph_impedance_get, NULL),
+};
+
+static int wcd934x_mbhc_init(struct snd_soc_component *component)
+{
+ struct wcd934x_ddata *data = dev_get_drvdata(component->dev->parent);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &wcd->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_SW_DET);
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_BUTTON_PRESS_DET);
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET);
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_MBHC_ELECT_INS_REM_DET);
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_HPH_PA_OCPL_FAULT);
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(data->irq_data,
+ WCD934X_IRQ_HPH_PA_OCPR_FAULT);
+
+ wcd->mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true);
+ if (IS_ERR(wcd->mbhc)) {
+ wcd->mbhc = NULL;
+ return -EINVAL;
+ }
+
+ snd_soc_add_component_controls(component, impedance_detect_controls,
+ ARRAY_SIZE(impedance_detect_controls));
+ snd_soc_add_component_controls(component, hph_type_detect_controls,
+ ARRAY_SIZE(hph_type_detect_controls));
+
+ return 0;
+}
static int wcd934x_comp_probe(struct snd_soc_component *component)
{
struct wcd934x_codec *wcd = dev_get_drvdata(component->dev);
@@ -2309,6 +3091,10 @@ static int wcd934x_comp_probe(struct snd_soc_component *component)
INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list);
wcd934x_init_dmic(component);
+
+ if (wcd934x_mbhc_init(component))
+ dev_err(component->dev, "Failed to Initialize MBHC\n");
+
return 0;
}
@@ -3756,6 +4542,7 @@ static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
int event)
{
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -3788,6 +4575,7 @@ static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
WCD934X_CDC_RX_PGA_MUTE_EN_MASK, 0x00);
break;
case SND_SOC_DAPM_PRE_PMD:
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF);
/* Enable DSD Mute before PA disable */
snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST,
WCD934X_HPH_OCP_DET_MASK,
@@ -3806,6 +4594,7 @@ static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
* disabled, then 20ms delay is needed after PA disable.
*/
usleep_range(20000, 20100);
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF);
break;
}
@@ -3817,6 +4606,7 @@ static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
int event)
{
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -3851,6 +4641,7 @@ static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
WCD934X_CDC_RX_PGA_MUTE_DISABLE);
break;
case SND_SOC_DAPM_PRE_PMD:
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_PRE_HPHR_PA_OFF);
snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST,
WCD934X_HPH_OCP_DET_MASK,
WCD934X_HPH_OCP_DET_DISABLE);
@@ -3868,6 +4659,7 @@ static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
* disabled, then 20ms delay is needed after PA disable.
*/
usleep_range(20000, 20100);
+ wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHR_PA_OFF);
break;
}
@@ -4323,6 +5115,29 @@ static int wcd934x_codec_enable_adc(struct snd_soc_dapm_widget *w,
return 0;
}
+static int wcd934x_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ int micb_num = w->shift;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ wcd934x_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* 1 msec delay as per HW requirement */
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ wcd934x_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = {
/* Analog Outputs */
SND_SOC_DAPM_OUTPUT("EAR"),
@@ -4778,13 +5593,17 @@ static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = {
wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0,
wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_SUPPLY("MIC BIAS1", WCD934X_ANA_MICB1, 6, 0, NULL,
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("MIC BIAS2", WCD934X_ANA_MICB2, 6, 0, NULL,
+ SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("MIC BIAS3", WCD934X_ANA_MICB3, 6, 0, NULL,
+ SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("MIC BIAS4", WCD934X_ANA_MICB4, 6, 0, NULL,
+ SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0,
+ wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("AMIC4_5 SEL", SND_SOC_NOPM, 0, 0, &tx_amic4_5),
@@ -4961,6 +5780,26 @@ static const struct snd_soc_dapm_route wcd934x_audio_map[] = {
{"SRC1", NULL, "IIR1"},
};
+static int wcd934x_codec_set_jack(struct snd_soc_component *comp,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (!wcd->mbhc)
+ return -ENOTSUPP;
+
+ if (jack && !wcd->mbhc_started) {
+ ret = wcd_mbhc_start(wcd->mbhc, &wcd->mbhc_cfg, jack);
+ wcd->mbhc_started = true;
+ } else if (wcd->mbhc_started) {
+ wcd_mbhc_stop(wcd->mbhc);
+ wcd->mbhc_started = false;
+ }
+
+ return ret;
+}
+
static const struct snd_soc_component_driver wcd934x_component_drv = {
.probe = wcd934x_comp_probe,
.remove = wcd934x_comp_remove,
@@ -4971,11 +5810,13 @@ static const struct snd_soc_component_driver wcd934x_component_drv = {
.num_dapm_widgets = ARRAY_SIZE(wcd934x_dapm_widgets),
.dapm_routes = wcd934x_audio_map,
.num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map),
+ .set_jack = wcd934x_codec_set_jack,
};
static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
{
struct device *dev = &wcd->sdev->dev;
+ struct wcd_mbhc_config *cfg = &wcd->mbhc_cfg;
struct device_node *ifc_dev_np;
ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0);
@@ -5001,6 +5842,18 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate",
&wcd->dmic_sample_rate);
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = WCD934X_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = wcd->micb2_mv;
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, cfg);
+
+
return 0;
}
@@ -5020,6 +5873,7 @@ static int wcd934x_codec_probe(struct platform_device *pdev)
wcd->extclk = data->extclk;
wcd->sdev = to_slim_device(data->dev);
mutex_init(&wcd->sysclk_mutex);
+ mutex_init(&wcd->micb_lock);
ret = wcd934x_codec_parse_data(wcd);
if (ret) {
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index b0a6d31299bb..c35673e7f420 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -30,7 +30,6 @@
#include <sound/wm2200.h>
#include "wm2200.h"
-#include "wmfw.h"
#include "wm_adsp.h"
#define WM2200_DSP_CONTROL_1 0x00
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 34b665895bdf..621598608bf0 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1989,7 +1989,7 @@ static unsigned int wm5102_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_5R,
};
-static struct snd_compress_ops wm5102_compress_ops = {
+static const struct snd_compress_ops wm5102_compress_ops = {
.open = wm5102_open,
.free = wm_adsp_compr_free,
.set_params = wm_adsp_compr_set_params,
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 76efca0fe515..5c2d45d05c97 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -2355,7 +2355,7 @@ static unsigned int wm5110_digital_vu[] = {
ARIZONA_DAC_DIGITAL_VOLUME_6R,
};
-static struct snd_compress_ops wm5110_compress_ops = {
+static const struct snd_compress_ops wm5110_compress_ops = {
.open = wm5110_open,
.free = wm_adsp_compr_free,
.set_params = wm_adsp_compr_set_params,
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 34080f497584..ba16bdf9e478 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3219,9 +3219,8 @@ static int wm8962_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t wm8962_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct wm8962_priv *wm8962 = dev_get_drvdata(dev);
long int time;
@@ -3236,7 +3235,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, wm8962_beep_set);
+static DEVICE_ATTR_WO(beep);
static void wm8962_init_beep(struct snd_soc_component *component)
{
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3dc119daf2f6..37aa020f23f6 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -303,9 +303,9 @@
#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff
#define HALO_MPU_VIO_ERR_SRC_SHIFT 0
-static struct wm_adsp_ops wm_adsp1_ops;
-static struct wm_adsp_ops wm_adsp2_ops[];
-static struct wm_adsp_ops wm_halo_ops;
+static const struct wm_adsp_ops wm_adsp1_ops;
+static const struct wm_adsp_ops wm_adsp2_ops[];
+static const struct wm_adsp_ops wm_halo_ops;
struct wm_adsp_buf {
struct list_head list;
@@ -824,7 +824,7 @@ const struct soc_enum wm_adsp_fw_enum[] = {
};
EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
-static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+static const struct wm_adsp_region *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
{
int i;
@@ -2240,7 +2240,7 @@ static void wmfw_v3_parse_id_header(struct wm_adsp *dsp,
}
static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions,
- int *type, __be32 *base)
+ const int *type, __be32 *base)
{
struct wm_adsp_alg_region *alg_region;
int i;
@@ -2487,7 +2487,7 @@ out:
static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id,
__be32 xm_base, __be32 ym_base)
{
- int types[] = {
+ static const int types[] = {
WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED,
WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED
};
@@ -4500,13 +4500,13 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data)
}
EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
-static struct wm_adsp_ops wm_adsp1_ops = {
+static const struct wm_adsp_ops wm_adsp1_ops = {
.validate_version = wm_adsp_validate_version,
.parse_sizes = wm_adsp1_parse_sizes,
.region_to_reg = wm_adsp_region_to_reg,
};
-static struct wm_adsp_ops wm_adsp2_ops[] = {
+static const struct wm_adsp_ops wm_adsp2_ops[] = {
{
.sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
.parse_sizes = wm_adsp2_parse_sizes,
@@ -4567,7 +4567,7 @@ static struct wm_adsp_ops wm_adsp2_ops[] = {
},
};
-static struct wm_adsp_ops wm_halo_ops = {
+static const struct wm_adsp_ops wm_halo_ops = {
.sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr),
.parse_sizes = wm_adsp2_parse_sizes,
.validate_version = wm_halo_validate_version,
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 1996350b817e..f22131d9cc29 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -64,7 +64,7 @@ struct wm_adsp {
struct regmap *regmap;
struct snd_soc_component *component;
- struct wm_adsp_ops *ops;
+ const struct wm_adsp_ops *ops;
unsigned int base;
unsigned int base_sysinfo;
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 0917d65d6921..8e05d092790e 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -119,6 +119,7 @@ config SND_SOC_FSL_RPMSG
tristate "NXP Audio Base On RPMSG support"
depends on COMMON_CLK
depends on RPMSG
+ depends on SND_IMX_SOC || SND_IMX_SOC = n
select SND_SOC_IMX_RPMSG if SND_IMX_SOC != n
help
Say Y if you want to add rpmsg audio support for the Freescale CPUs.
@@ -350,6 +351,19 @@ config SND_SOC_IMX_RPMSG
Say Y if you want to add support for SoC audio on an i.MX board with
a rpmsg devices.
+config SND_SOC_IMX_CARD
+ tristate "SoC Audio Graph Sound Card support for i.MX boards"
+ depends on OF && I2C
+ select SND_SOC_AK4458
+ select SND_SOC_AK5558
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SAI
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables audio sound card support for i.MX boards
+ with OF-graph DT bindings.
+ It also support DPCM of single CPU multi Codec ststem.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index f146ce464acd..b54beb1a66fa 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -71,6 +71,7 @@ snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-audmix-objs := imx-audmix.o
snd-soc-imx-hdmi-objs := imx-hdmi.o
snd-soc-imx-rpmsg-objs := imx-rpmsg.o
+snd-soc-imx-card-objs := imx-card.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
@@ -79,3 +80,4 @@ obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
+obj-$(CONFIG_SND_SOC_IMX_CARD) += snd-soc-imx-card.o
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index c631de325a6e..2a76714eb8e6 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -49,6 +49,7 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
* @imx: for imx platform
* @shared_root_clock: flag of sharing a clock source with others;
* so the driver shouldn't set root clock rate
+ * @raw_capture_mode: if raw capture mode support
* @interrupts: interrupt number
* @tx_burst: tx maxburst size
* @rx_burst: rx maxburst size
@@ -57,6 +58,7 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
struct fsl_spdif_soc_data {
bool imx;
bool shared_root_clock;
+ bool raw_capture_mode;
u32 interrupts;
u32 tx_burst;
u32 rx_burst;
@@ -136,6 +138,7 @@ struct fsl_spdif_priv {
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
.imx = false,
.shared_root_clock = false,
+ .raw_capture_mode = false,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
@@ -145,6 +148,7 @@ static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
static struct fsl_spdif_soc_data fsl_spdif_imx35 = {
.imx = true,
.shared_root_clock = false,
+ .raw_capture_mode = false,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
@@ -154,6 +158,7 @@ static struct fsl_spdif_soc_data fsl_spdif_imx35 = {
static struct fsl_spdif_soc_data fsl_spdif_imx6sx = {
.imx = true,
.shared_root_clock = true,
+ .raw_capture_mode = false,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
@@ -164,12 +169,23 @@ static struct fsl_spdif_soc_data fsl_spdif_imx6sx = {
static struct fsl_spdif_soc_data fsl_spdif_imx8qm = {
.imx = true,
.shared_root_clock = true,
+ .raw_capture_mode = false,
.interrupts = 2,
.tx_burst = 2, /* Applied for EDMA */
.rx_burst = 2, /* Applied for EDMA */
.tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */
};
+static struct fsl_spdif_soc_data fsl_spdif_imx8mm = {
+ .imx = true,
+ .shared_root_clock = false,
+ .raw_capture_mode = true,
+ .interrupts = 1,
+ .tx_burst = FSL_SPDIF_TXFIFO_WML,
+ .rx_burst = FSL_SPDIF_RXFIFO_WML,
+ .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
+};
+
/* Check if clk is a root clock that does not share clock source with others */
static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk)
{
@@ -846,6 +862,39 @@ static int fsl_spdif_tx_vbit_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int fsl_spdif_rx_rcm_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ val = (val & SCR_RAW_CAPTURE_MODE) ? 1 : 0;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = (ucontrol->value.integer.value[0] ? SCR_RAW_CAPTURE_MODE : 0);
+
+ if (val)
+ cpu_dai->driver->capture.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ else
+ cpu_dai->driver->capture.formats &= ~SNDRV_PCM_FMTBIT_S32_LE;
+
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_RAW_CAPTURE_MODE, val);
+
+ return 0;
+}
+
/* DPLL lock information */
static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1029,6 +1078,19 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
},
};
+static struct snd_kcontrol_new fsl_spdif_ctrls_rcm[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Raw Capture Mode",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = fsl_spdif_rx_rcm_get,
+ .put = fsl_spdif_rx_rcm_put,
+ },
+};
+
static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
@@ -1038,6 +1100,10 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+ if (spdif_private->soc->raw_capture_mode)
+ snd_soc_add_dai_controls(dai, fsl_spdif_ctrls_rcm,
+ ARRAY_SIZE(fsl_spdif_ctrls_rcm));
+
/*Clear the val bit for Tx*/
regmap_update_bits(spdif_private->regmap, REG_SPDIF_SCR,
SCR_VAL_MASK, SCR_VAL_CLEAR);
@@ -1476,6 +1542,7 @@ static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, },
{ .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, },
{ .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
+ { .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
{}
};
MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
index d5f1dfd58740..bff8290e71f2 100644
--- a/sound/soc/fsl/fsl_spdif.h
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -63,6 +63,7 @@
#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_RAW_CAPTURE_MODE BIT(14)
#define SCR_LOW_POWER (1 << 13)
#define SCR_SOFT_RESET (1 << 12)
#define SCR_TXFIFO_CTRL_OFFSET 10
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 6cb558165848..df7c189d97dd 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -736,7 +736,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr)
/* clean current page, including data memory */
memset_io(xcvr->ram_addr, 0, size);
}
- };
+ }
err_firmware:
release_firmware(fw);
diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c
index 50099bcaa9cd..905c3a071300 100644
--- a/sound/soc/fsl/imx-audio-rpmsg.c
+++ b/sound/soc/fsl/imx-audio-rpmsg.c
@@ -122,17 +122,7 @@ static struct rpmsg_driver imx_audio_rpmsg_driver = {
.remove = imx_audio_rpmsg_remove,
};
-static int __init imx_audio_rpmsg_init(void)
-{
- return register_rpmsg_driver(&imx_audio_rpmsg_driver);
-}
-
-static void __exit imx_audio_rpmsg_exit(void)
-{
- unregister_rpmsg_driver(&imx_audio_rpmsg_driver);
-}
-module_init(imx_audio_rpmsg_init);
-module_exit(imx_audio_rpmsg_exit);
+module_rpmsg_driver(imx_audio_rpmsg_driver);
MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface");
MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
new file mode 100644
index 000000000000..58fd0639a069
--- /dev/null
+++ b/sound/soc/fsl/imx-card.c
@@ -0,0 +1,844 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2021 NXP
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <sound/soc-dapm.h>
+#include <sound/simple_card_utils.h>
+
+#include "fsl_sai.h"
+
+enum codec_type {
+ CODEC_DUMMY = 0,
+ CODEC_AK5558 = 1,
+ CODEC_AK4458,
+ CODEC_AK4497,
+ CODEC_AK5552,
+};
+
+/*
+ * Mapping LRCK fs and frame width, table 3 & 4 in datasheet
+ * @rmin: min rate
+ * @rmax: max rate
+ * @wmin: min frame ratio
+ * @wmax: max frame ratio
+ */
+struct imx_akcodec_fs_mul {
+ unsigned int rmin;
+ unsigned int rmax;
+ unsigned int wmin;
+ unsigned int wmax;
+};
+
+/*
+ * Mapping TDM mode and frame width
+ */
+struct imx_akcodec_tdm_fs_mul {
+ unsigned int min;
+ unsigned int max;
+ unsigned int mul;
+};
+
+/*
+ * struct imx_card_plat_data - specific info for codecs
+ *
+ * @fs_mul: ratio of mclk/fs for normal mode
+ * @tdm_fs_mul: ratio of mclk/fs for tdm mode
+ * @support_rates: supported sample rate
+ * @support_tdm_rates: supported sample rate for tdm mode
+ * @support_channels: supported channels
+ * @support_tdm_channels: supported channels for tdm mode
+ * @num_fs_mul: ARRAY_SIZE of fs_mul
+ * @num_tdm_fs_mul: ARRAY_SIZE of tdm_fs_mul
+ * @num_rates: ARRAY_SIZE of support_rates
+ * @num_tdm_rates: ARRAY_SIZE of support_tdm_rates
+ * @num_channels: ARRAY_SIZE of support_channels
+ * @num_tdm_channels: ARRAY_SIZE of support_tdm_channels
+ * @type: codec type
+ */
+struct imx_card_plat_data {
+ struct imx_akcodec_fs_mul *fs_mul;
+ struct imx_akcodec_tdm_fs_mul *tdm_fs_mul;
+ const u32 *support_rates;
+ const u32 *support_tdm_rates;
+ const u32 *support_channels;
+ const u32 *support_tdm_channels;
+ unsigned int num_fs_mul;
+ unsigned int num_tdm_fs_mul;
+ unsigned int num_rates;
+ unsigned int num_tdm_rates;
+ unsigned int num_channels;
+ unsigned int num_tdm_channels;
+ unsigned int num_codecs;
+ enum codec_type type;
+};
+
+/*
+ * struct dai_link_data - specific info for dai link
+ *
+ * @slots: slot number
+ * @slot_width: slot width value
+ * @cpu_sysclk_id: sysclk id for cpu dai
+ * @one2one_ratio: true if mclk equal to bclk
+ */
+struct dai_link_data {
+ unsigned int slots;
+ unsigned int slot_width;
+ unsigned int cpu_sysclk_id;
+ bool one2one_ratio;
+};
+
+/*
+ * struct imx_card_data - platform device data
+ *
+ * @plat_data: pointer of imx_card_plat_data
+ * @dapm_routes: pointer of dapm_routes
+ * @link_data: private data for dai link
+ * @card: card instance
+ * @num_dapm_routes: number of dapm_routes
+ * @asrc_rate: asrc rates
+ * @asrc_format: asrc format
+ */
+struct imx_card_data {
+ struct imx_card_plat_data *plat_data;
+ struct snd_soc_dapm_route *dapm_routes;
+ struct dai_link_data *link_data;
+ struct snd_soc_card card;
+ int num_dapm_routes;
+ u32 asrc_rate;
+ u32 asrc_format;
+};
+
+static struct imx_akcodec_fs_mul ak4458_fs_mul[] = {
+ /* Normal, < 32kHz */
+ { .rmin = 8000, .rmax = 24000, .wmin = 1024, .wmax = 1024, },
+ /* Normal, 32kHz */
+ { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, },
+ /* Normal */
+ { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, },
+ /* Double */
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 512, },
+ /* Quad */
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 256, },
+ /* Oct */
+ { .rmin = 352800, .rmax = 384000, .wmin = 32, .wmax = 128, },
+ /* Hex */
+ { .rmin = 705600, .rmax = 768000, .wmin = 16, .wmax = 64, },
+};
+
+static struct imx_akcodec_tdm_fs_mul ak4458_tdm_fs_mul[] = {
+ /*
+ * Table 13 - Audio Interface Format
+ * For TDM mode, MCLK should is set to
+ * obtained from 2 * slots * slot_width
+ */
+ { .min = 128, .max = 128, .mul = 256 }, /* TDM128 */
+ { .min = 256, .max = 256, .mul = 512 }, /* TDM256 */
+ { .min = 512, .max = 512, .mul = 1024 }, /* TDM512 */
+};
+
+static struct imx_akcodec_fs_mul ak4497_fs_mul[] = {
+ /**
+ * Table 7 - mapping multiplier and speed mode
+ * Tables 8 & 9 - mapping speed mode and LRCK fs
+ */
+ { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, }, /* Normal, <= 32kHz */
+ { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, /* Normal */
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */
+ { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */
+ { .rmin = 705600, .rmax = 768000, .wmin = 64, .wmax = 64, }, /* Hex */
+};
+
+/*
+ * Auto MCLK selection based on LRCK for Normal Mode
+ * (Table 4 from datasheet)
+ */
+static struct imx_akcodec_fs_mul ak5558_fs_mul[] = {
+ { .rmin = 8000, .rmax = 32000, .wmin = 1024, .wmax = 1024, },
+ { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, },
+ { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, },
+ { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, },
+ { .rmin = 352800, .rmax = 384000, .wmin = 64, .wmax = 64, },
+ { .rmin = 705600, .rmax = 768000, .wmin = 32, .wmax = 32, },
+};
+
+/*
+ * MCLK and BCLK selection based on TDM mode
+ * because of SAI we also add the restriction: MCLK >= 2 * BCLK
+ * (Table 9 from datasheet)
+ */
+static struct imx_akcodec_tdm_fs_mul ak5558_tdm_fs_mul[] = {
+ { .min = 128, .max = 128, .mul = 256 },
+ { .min = 256, .max = 256, .mul = 512 },
+ { .min = 512, .max = 512, .mul = 1024 },
+};
+
+static const u32 akcodec_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 352800, 384000, 705600, 768000,
+};
+
+static const u32 akcodec_tdm_rates[] = {
+ 8000, 16000, 32000, 48000, 96000,
+};
+
+static const u32 ak4458_channels[] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16,
+};
+
+static const u32 ak4458_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 16,
+};
+
+static const u32 ak5558_channels[] = {
+ 1, 2, 4, 6, 8,
+};
+
+static const u32 ak5558_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+};
+
+static bool format_is_dsd(struct snd_pcm_hw_params *params)
+{
+ snd_pcm_format_t format = params_format(params);
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool format_is_tdm(struct dai_link_data *link_data)
+{
+ if (link_data->slots > 2)
+ return true;
+ else
+ return false;
+}
+
+static bool codec_is_akcodec(unsigned int type)
+{
+ switch (type) {
+ case CODEC_AK4458:
+ case CODEC_AK4497:
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card);
+ const struct imx_card_plat_data *plat_data = data->plat_data;
+ struct dai_link_data *link_data = &data->link_data[rtd->num];
+ unsigned int width = link_data->slots * link_data->slot_width;
+ unsigned int rate = params_rate(params);
+ int i;
+
+ if (format_is_tdm(link_data)) {
+ for (i = 0; i < plat_data->num_tdm_fs_mul; i++) {
+ /* min = max = slots * slots_width */
+ if (width != plat_data->tdm_fs_mul[i].min)
+ continue;
+ return rate * plat_data->tdm_fs_mul[i].mul;
+ }
+ } else {
+ for (i = 0; i < plat_data->num_fs_mul; i++) {
+ if (rate >= plat_data->fs_mul[i].rmin &&
+ rate <= plat_data->fs_mul[i].rmax) {
+ width = max(width, plat_data->fs_mul[i].wmin);
+ width = min(width, plat_data->fs_mul[i].wmax);
+
+ /* Adjust SAI bclk:mclk ratio */
+ width *= link_data->one2one_ratio ? 1 : 2;
+
+ return rate * width;
+ }
+ }
+ }
+
+ /* Let DAI manage clk frequency by default */
+ return 0;
+}
+
+static int imx_aif_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 = asoc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct dai_link_data *link_data = &data->link_data[rtd->num];
+ struct imx_card_plat_data *plat_data = data->plat_data;
+ struct device *dev = card->dev;
+ struct snd_soc_dai *codec_dai;
+ unsigned long mclk_freq;
+ unsigned int fmt = rtd->dai_link->dai_fmt;
+ unsigned int slots, slot_width;
+ int ret, i;
+
+ slots = link_data->slots;
+ slot_width = link_data->slot_width;
+
+ if (!format_is_tdm(link_data)) {
+ if (format_is_dsd(params)) {
+ slots = 1;
+ slot_width = params_width(params);
+ fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+ SND_SOC_DAIFMT_PDM;
+ } else {
+ slots = 2;
+ slot_width = params_physical_width(params);
+ fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) |
+ SND_SOC_DAIFMT_I2S;
+ }
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+ BIT(slots) - 1,
+ BIT(slots) - 1,
+ slots, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai[%d] fmt: %d\n", i, ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ BIT(slots) - 1,
+ BIT(slots) - 1,
+ slots, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n", i, ret);
+ return ret;
+ }
+ }
+
+ /* Set MCLK freq */
+ if (codec_is_akcodec(plat_data->type))
+ mclk_freq = akcodec_get_mclk_rate(substream, params);
+ else
+ mclk_freq = params_rate(params) * slots * slot_width;
+ /* Use the maximum freq from DSD512 (512*44100 = 22579200) */
+ if (format_is_dsd(params))
+ mclk_freq = 22579200;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, link_data->cpu_sysclk_id, mclk_freq,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n", mclk_freq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ak5558_hw_rule_rate(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *r)
+{
+ struct dai_link_data *link_data = r->private;
+ struct snd_interval t = { .min = 8000, .max = 8000, };
+ unsigned long mclk_freq;
+ unsigned int fs;
+ int i;
+
+ fs = hw_param_interval(p, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ fs *= link_data->slots;
+
+ /* Identify maximum supported rate */
+ for (i = 0; i < ARRAY_SIZE(akcodec_rates); i++) {
+ mclk_freq = fs * akcodec_rates[i];
+ /* Adjust SAI bclk:mclk ratio */
+ mclk_freq *= link_data->one2one_ratio ? 1 : 2;
+
+ /* Skip rates for which MCLK is beyond supported value */
+ if (mclk_freq > 36864000)
+ continue;
+
+ if (t.max < akcodec_rates[i])
+ t.max = akcodec_rates[i];
+ }
+
+ return snd_interval_refine(hw_param_interval(p, r->var), &t);
+}
+
+static int imx_aif_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct dai_link_data *link_data = &data->link_data[rtd->num];
+ static struct snd_pcm_hw_constraint_list constraint_rates;
+ static struct snd_pcm_hw_constraint_list constraint_channels;
+ int ret = 0;
+
+ if (format_is_tdm(link_data)) {
+ constraint_channels.list = data->plat_data->support_tdm_channels;
+ constraint_channels.count = data->plat_data->num_tdm_channels;
+ constraint_rates.list = data->plat_data->support_tdm_rates;
+ constraint_rates.count = data->plat_data->num_tdm_rates;
+ } else {
+ constraint_channels.list = data->plat_data->support_channels;
+ constraint_channels.count = data->plat_data->num_channels;
+ constraint_rates.list = data->plat_data->support_rates;
+ constraint_rates.count = data->plat_data->num_rates;
+ }
+
+ if (constraint_channels.count) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraint_channels);
+ if (ret)
+ return ret;
+ }
+
+ if (constraint_rates.count) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraint_rates);
+ if (ret)
+ return ret;
+ }
+
+ if (data->plat_data->type == CODEC_AK5558)
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ ak5558_hw_rule_rate, link_data,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
+
+ return ret;
+}
+
+static struct snd_soc_ops imx_aif_ops = {
+ .hw_params = imx_aif_hw_params,
+ .startup = imx_aif_startup,
+};
+
+static struct snd_soc_ops imx_aif_ops_be = {
+ .hw_params = imx_aif_hw_params,
+};
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct imx_card_data *data = snd_soc_card_get_drvdata(card);
+ struct snd_interval *rate;
+ struct snd_mask *mask;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ rate->max = data->asrc_rate;
+ rate->min = data->asrc_rate;
+
+ mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(mask);
+ snd_mask_set(mask, data->asrc_format);
+
+ return 0;
+}
+
+static int imx_card_parse_of(struct imx_card_data *data)
+{
+ struct imx_card_plat_data *plat_data = data->plat_data;
+ struct snd_soc_card *card = &data->card;
+ struct snd_soc_dai_link_component *dlc;
+ struct device_node *platform = NULL;
+ struct device_node *codec = NULL;
+ struct device_node *cpu = NULL;
+ struct device_node *np;
+ struct device *dev = card->dev;
+ struct snd_soc_dai_link *link;
+ struct dai_link_data *link_data;
+ struct of_phandle_args args;
+ int ret, num_links;
+ u32 width;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret) {
+ dev_err(dev, "Error parsing card name: %d\n", ret);
+ return ret;
+ }
+
+ /* DAPM routes */
+ if (of_property_read_bool(dev->of_node, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret)
+ return ret;
+ }
+
+ /* Populate links */
+ num_links = of_get_child_count(dev->of_node);
+
+ /* Allocate the DAI link array */
+ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+
+ data->link_data = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ if (!data->link_data)
+ return -ENOMEM;
+
+ card->num_links = num_links;
+ link = card->dai_link;
+ link_data = data->link_data;
+
+ for_each_child_of_node(dev->of_node, np) {
+ dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ ret = -ENOMEM;
+ goto err_put_np;
+ }
+
+ link->cpus = &dlc[0];
+ link->platforms = &dlc[1];
+
+ link->num_cpus = 1;
+ link->num_platforms = 1;
+
+ ret = of_property_read_string(np, "link-name", &link->name);
+ if (ret) {
+ dev_err(card->dev, "error getting codec dai_link name\n");
+ goto err_put_np;
+ }
+
+ cpu = of_get_child_by_name(np, "cpu");
+ if (!cpu) {
+ dev_err(dev, "%s: Can't find cpu DT node\n", link->name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = of_parse_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells", 0, &args);
+ if (ret) {
+ dev_err(card->dev, "%s: error getting cpu phandle\n", link->name);
+ goto err;
+ }
+
+ if (of_node_name_eq(args.np, "sai")) {
+ /* sai sysclk id */
+ link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1;
+
+ /* sai may support mclk/bclk = 1 */
+ if (of_find_property(np, "fsl,mclk-equal-bclk", NULL))
+ link_data->one2one_ratio = true;
+ }
+
+ link->cpus->of_node = args.np;
+ link->platforms->of_node = link->cpus->of_node;
+ link->id = args.args[0];
+
+ ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(card->dev, "%s: error getting cpu dai name: %d\n",
+ link->name, ret);
+ goto err;
+ }
+
+ codec = of_get_child_by_name(np, "codec");
+ if (codec) {
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "%s: codec dai not found: %d\n",
+ link->name, ret);
+ goto err;
+ }
+
+ plat_data->num_codecs = link->num_codecs;
+
+ /* Check the akcodec type */
+ if (!strcmp(link->codecs->dai_name, "ak4458-aif"))
+ plat_data->type = CODEC_AK4458;
+ else if (!strcmp(link->codecs->dai_name, "ak4497-aif"))
+ plat_data->type = CODEC_AK4497;
+ else if (!strcmp(link->codecs->dai_name, "ak5558-aif"))
+ plat_data->type = CODEC_AK5558;
+ else if (!strcmp(link->codecs->dai_name, "ak5552-aif"))
+ plat_data->type = CODEC_AK5552;
+
+ } else {
+ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ link->codecs = dlc;
+ link->num_codecs = 1;
+
+ link->codecs->dai_name = "snd-soc-dummy-dai";
+ link->codecs->name = "snd-soc-dummy";
+ }
+
+ if (!strncmp(link->name, "HiFi-ASRC-FE", 12)) {
+ /* DPCM frontend */
+ link->dynamic = 1;
+ link->dpcm_merged_chan = 1;
+
+ ret = of_property_read_u32(args.np, "fsl,asrc-rate", &data->asrc_rate);
+ if (ret) {
+ dev_err(dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = of_property_read_u32(args.np, "fsl,asrc-format", &data->asrc_format);
+ if (ret) {
+ /* Fallback to old binding; translate to asrc_format */
+ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(dev,
+ "failed to decide output format\n");
+ goto err;
+ }
+
+ if (width == 24)
+ data->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ data->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
+ } else if (!strncmp(link->name, "HiFi-ASRC-BE", 12)) {
+ /* DPCM backend */
+ link->no_pcm = 1;
+ link->platforms->of_node = NULL;
+ link->platforms->name = "snd-soc-dummy";
+
+ link->be_hw_params_fixup = be_hw_params_fixup;
+ link->ops = &imx_aif_ops_be;
+ } else {
+ link->ops = &imx_aif_ops;
+ }
+
+ if (link->no_pcm || link->dynamic)
+ snd_soc_dai_link_set_capabilities(link);
+
+ /* Get dai fmt */
+ ret = asoc_simple_parse_daifmt(dev, np, codec,
+ NULL, &link->dai_fmt);
+ if (ret)
+ link->dai_fmt = SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS |
+ SND_SOC_DAIFMT_I2S;
+
+ /* Get tdm slot */
+ snd_soc_of_parse_tdm_slot(np, NULL, NULL,
+ &link_data->slots,
+ &link_data->slot_width);
+ /* default value */
+ if (!link_data->slots)
+ link_data->slots = 2;
+
+ if (!link_data->slot_width)
+ link_data->slot_width = 32;
+
+ link->ignore_pmdown_time = 1;
+ link->stream_name = link->name;
+ link++;
+ link_data++;
+
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+ }
+
+ return 0;
+err:
+ of_node_put(cpu);
+ of_node_put(codec);
+ of_node_put(platform);
+err_put_np:
+ of_node_put(np);
+ return ret;
+}
+
+static int imx_card_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *link_be = NULL, *link;
+ struct imx_card_plat_data *plat_data;
+ struct imx_card_data *data;
+ int ret, i;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ plat_data = devm_kzalloc(&pdev->dev, sizeof(*plat_data), GFP_KERNEL);
+ if (!plat_data)
+ return -ENOMEM;
+
+ data->plat_data = plat_data;
+ data->card.dev = &pdev->dev;
+
+ dev_set_drvdata(&pdev->dev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = imx_card_parse_of(data);
+ if (ret)
+ return ret;
+
+ data->num_dapm_routes = plat_data->num_codecs + 1;
+ data->dapm_routes = devm_kcalloc(&pdev->dev, data->num_dapm_routes,
+ sizeof(struct snd_soc_dapm_route),
+ GFP_KERNEL);
+ if (!data->dapm_routes)
+ return -ENOMEM;
+
+ /* configure the dapm routes */
+ switch (plat_data->type) {
+ case CODEC_AK4458:
+ case CODEC_AK4497:
+ if (plat_data->num_codecs == 1) {
+ data->dapm_routes[0].sink = "Playback";
+ data->dapm_routes[0].source = "CPU-Playback";
+ i = 1;
+ } else {
+ for (i = 0; i < plat_data->num_codecs; i++) {
+ data->dapm_routes[i].sink =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+ i + 1, "Playback");
+ data->dapm_routes[i].source = "CPU-Playback";
+ }
+ }
+ data->dapm_routes[i].sink = "CPU-Playback";
+ data->dapm_routes[i].source = "ASRC-Playback";
+ break;
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ if (plat_data->num_codecs == 1) {
+ data->dapm_routes[0].sink = "CPU-Capture";
+ data->dapm_routes[0].source = "Capture";
+ i = 1;
+ } else {
+ for (i = 0; i < plat_data->num_codecs; i++) {
+ data->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
+ i + 1, "Capture");
+ data->dapm_routes[i].sink = "CPU-Capture";
+ }
+ }
+ data->dapm_routes[i].sink = "ASRC-Capture";
+ data->dapm_routes[i].source = "CPU-Capture";
+ break;
+ default:
+ break;
+ }
+
+ /* default platform data for akcodecs */
+ if (codec_is_akcodec(plat_data->type)) {
+ plat_data->support_rates = akcodec_rates;
+ plat_data->num_rates = ARRAY_SIZE(akcodec_rates);
+ plat_data->support_tdm_rates = akcodec_tdm_rates;
+ plat_data->num_tdm_rates = ARRAY_SIZE(akcodec_tdm_rates);
+
+ switch (plat_data->type) {
+ case CODEC_AK4458:
+ plat_data->fs_mul = ak4458_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak4458_fs_mul);
+ plat_data->tdm_fs_mul = ak4458_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak4458_tdm_fs_mul);
+ plat_data->support_channels = ak4458_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+ plat_data->support_tdm_channels = ak4458_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(ak4458_tdm_channels);
+ break;
+ case CODEC_AK4497:
+ plat_data->fs_mul = ak4497_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak4497_fs_mul);
+ plat_data->support_channels = ak4458_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak4458_channels);
+ break;
+ case CODEC_AK5558:
+ case CODEC_AK5552:
+ plat_data->fs_mul = ak5558_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(ak5558_fs_mul);
+ plat_data->tdm_fs_mul = ak5558_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak5558_tdm_fs_mul);
+ plat_data->support_channels = ak5558_channels;
+ plat_data->num_channels = ARRAY_SIZE(ak5558_channels);
+ plat_data->support_tdm_channels = ak5558_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(ak5558_tdm_channels);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* with asrc as front end */
+ if (data->card.num_links == 3) {
+ data->card.dapm_routes = data->dapm_routes;
+ data->card.num_dapm_routes = data->num_dapm_routes;
+ for_each_card_prelinks(&data->card, i, link) {
+ if (link->no_pcm == 1)
+ link_be = link;
+ }
+ for_each_card_prelinks(&data->card, i, link) {
+ if (link->dynamic == 1 && link_be) {
+ link->dpcm_playback = link_be->dpcm_playback;
+ link->dpcm_capture = link_be->dpcm_capture;
+ }
+ }
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id imx_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-card", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, imx_card_dt_ids);
+
+static struct platform_driver imx_card_driver = {
+ .driver = {
+ .name = "imx-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_card_dt_ids,
+ },
+ .probe = imx_card_probe,
+};
+module_platform_driver(imx_card_driver);
+
+MODULE_DESCRIPTION("Freescale i.MX ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-card");
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
index fad1eb6253d5..1981dcd7e930 100644
--- a/sound/soc/fsl/imx-es8328.c
+++ b/sound/soc/fsl/imx-es8328.c
@@ -193,7 +193,7 @@ static int imx_es8328_probe(struct platform_device *pdev)
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
- ret = snd_soc_register_card(&data->card);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
dev_err(dev, "Unable to register: %d\n", ret);
goto put_device;
@@ -209,15 +209,6 @@ fail:
return ret;
}
-static int imx_es8328_remove(struct platform_device *pdev)
-{
- struct imx_es8328_data *data = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(&data->card);
-
- return 0;
-}
-
static const struct of_device_id imx_es8328_dt_ids[] = {
{ .compatible = "fsl,imx-audio-es8328", },
{ /* sentinel */ }
@@ -230,7 +221,6 @@ static struct platform_driver imx_es8328_driver = {
.of_match_table = imx_es8328_dt_ids,
},
.probe = imx_es8328_probe,
- .remove = imx_es8328_remove,
};
module_platform_driver(imx_es8328_driver);
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index 875c0d6df339..6d6c44cf3451 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -161,10 +161,10 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
msg->s_msg.param.format = RPMSG_S24_LE;
break;
case SNDRV_PCM_FORMAT_DSD_U16_LE:
- msg->s_msg.param.format = SNDRV_PCM_FORMAT_DSD_U16_LE;
+ msg->s_msg.param.format = RPMSG_DSD_U16_LE;
break;
case SNDRV_PCM_FORMAT_DSD_U32_LE:
- msg->s_msg.param.format = SNDRV_PCM_FORMAT_DSD_U32_LE;
+ msg->s_msg.param.format = RPMSG_DSD_U32_LE;
break;
default:
msg->s_msg.param.format = RPMSG_S32_LE;
@@ -544,7 +544,7 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
struct rpmsg_msg *msg;
unsigned long flags;
int buffer_tail = 0;
- int written_num = 0;
+ int written_num;
if (!rpmsg->force_lpa)
return 0;
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.h b/sound/soc/fsl/imx-pcm-rpmsg.h
index 308d153920a3..8286b55f00ae 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.h
+++ b/sound/soc/fsl/imx-pcm-rpmsg.h
@@ -328,9 +328,9 @@
#define RPMSG_S16_LE 0x0
#define RPMSG_S24_LE 0x1
#define RPMSG_S32_LE 0x2
-#define RPMSG_DSD_U16_LE 0x3
+#define RPMSG_DSD_U16_LE 49 /* SNDRV_PCM_FORMAT_DSD_U16_LE */
#define RPMSG_DSD_U24_LE 0x4
-#define RPMSG_DSD_U32_LE 0x5
+#define RPMSG_DSD_U32_LE 50 /* SNDRV_PCM_FORMAT_DSD_U32_LE */
#define RPMSG_CH_LEFT 0x0
#define RPMSG_CH_RIGHT 0x1
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index 5a9a470d203f..f0cae8c59d54 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -137,7 +137,6 @@ fail:
static struct platform_driver imx_rpmsg_driver = {
.driver = {
.name = "imx-audio-rpmsg",
- .owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = imx_rpmsg_probe,
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 2c8a2fcb7922..5e71382467e8 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -209,7 +209,7 @@ static void graph_parse_mclk_fs(struct device_node *top,
static int graph_parse_node(struct asoc_simple_priv *priv,
struct device_node *ep,
struct link_info *li,
- int is_cpu)
+ int *cpu)
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *top = dev->of_node;
@@ -217,9 +217,9 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
struct snd_soc_dai_link_component *dlc;
struct asoc_simple_dai *dai;
- int ret, single = 0;
+ int ret;
- if (is_cpu) {
+ if (cpu) {
dlc = asoc_link_to_cpu(dai_link, 0);
dai = simple_props_to_dai_cpu(dai_props, 0);
} else {
@@ -229,7 +229,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(top, ep, dai_props);
- ret = asoc_simple_parse_dai(ep, dlc, &single);
+ ret = asoc_simple_parse_dai(ep, dlc, cpu);
if (ret < 0)
return ret;
@@ -241,9 +241,6 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
if (ret < 0)
return ret;
- if (is_cpu)
- asoc_simple_canonicalize_cpu(dlc, single);
-
return 0;
}
@@ -276,33 +273,29 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
- struct snd_soc_card *card = simple_priv_to_card(priv);
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
struct device_node *top = dev->of_node;
struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
- struct device_node *port;
- struct device_node *ports;
- struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
- struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
char dai_name[64];
int ret;
- port = of_get_parent(ep);
- ports = of_get_parent(port);
-
dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
if (li->cpu) {
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
+ int is_single_links = 0;
+
/* Codec is dummy */
/* FE settings */
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
- ret = graph_parse_node(priv, cpu_ep, li, 1);
+ ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
if (ret)
- goto out_put_node;
+ return ret;
snprintf(dai_name, sizeof(dai_name),
"fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
@@ -318,8 +311,13 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
*/
if (card->component_chaining && !soc_component_is_pcm(cpus))
dai_link->no_pcm = 1;
+
+ asoc_simple_canonicalize_cpu(cpus, is_single_links);
} else {
- struct snd_soc_codec_conf *cconf;
+ struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
+ struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
+ struct device_node *port;
+ struct device_node *ports;
/* CPU is dummy */
@@ -327,22 +325,25 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
- cconf = simple_props_to_codec_conf(dai_props, 0);
-
- ret = graph_parse_node(priv, codec_ep, li, 0);
+ ret = graph_parse_node(priv, codec_ep, li, NULL);
if (ret < 0)
- goto out_put_node;
+ return ret;
snprintf(dai_name, sizeof(dai_name),
"be.%pOFP.%s", codecs->of_node, codecs->dai_name);
/* check "prefix" from top node */
+ port = of_get_parent(ep);
+ ports = of_get_parent(port);
snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
"prefix");
if (of_node_name_eq(ports, "ports"))
snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node,
"prefix");
+
+ of_node_put(ports);
+ of_node_put(port);
}
graph_parse_convert(dev, ep, &dai_props->adata);
@@ -351,11 +352,8 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
-out_put_node:
li->link++;
- of_node_put(ports);
- of_node_put(port);
return ret;
}
@@ -369,20 +367,23 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv,
struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
char dai_name[64];
- int ret;
+ int ret, is_single_links = 0;
dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
- ret = graph_parse_node(priv, cpu_ep, li, 1);
+ ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
if (ret < 0)
return ret;
- ret = graph_parse_node(priv, codec_ep, li, 0);
+ ret = graph_parse_node(priv, codec_ep, li, NULL);
if (ret < 0)
return ret;
snprintf(dai_name, sizeof(dai_name),
"%s-%s", cpus->dai_name, codecs->dai_name);
+
+ asoc_simple_canonicalize_cpu(cpus, is_single_links);
+
ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
if (ret < 0)
return ret;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index a1373be4558f..0015f534d42d 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -93,12 +93,11 @@ static void simple_parse_convert(struct device *dev,
}
static void simple_parse_mclk_fs(struct device_node *top,
- struct device_node *cpu,
- struct device_node *codec,
+ struct device_node *np,
struct simple_dai_props *props,
char *prefix)
{
- struct device_node *node = of_get_parent(cpu);
+ struct device_node *node = of_get_parent(np);
char prop[128];
snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX);
@@ -106,12 +105,71 @@ static void simple_parse_mclk_fs(struct device_node *top,
snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
of_property_read_u32(node, prop, &props->mclk_fs);
- of_property_read_u32(cpu, prop, &props->mclk_fs);
- of_property_read_u32(codec, prop, &props->mclk_fs);
+ of_property_read_u32(np, prop, &props->mclk_fs);
of_node_put(node);
}
+static int simple_parse_node(struct asoc_simple_priv *priv,
+ struct device_node *np,
+ struct link_info *li,
+ char *prefix,
+ int *cpu)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *top = dev->of_node;
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
+ struct snd_soc_dai_link_component *dlc;
+ struct asoc_simple_dai *dai;
+ int ret;
+
+ if (cpu) {
+ dlc = asoc_link_to_cpu(dai_link, 0);
+ dai = simple_props_to_dai_cpu(dai_props, 0);
+ } else {
+ dlc = asoc_link_to_codec(dai_link, 0);
+ dai = simple_props_to_dai_codec(dai_props, 0);
+ }
+
+ simple_parse_mclk_fs(top, np, dai_props, prefix);
+
+ ret = asoc_simple_parse_dai(np, dlc, cpu);
+ if (ret)
+ return ret;
+
+ ret = asoc_simple_parse_clk(dev, np, dai, dlc);
+ if (ret)
+ return ret;
+
+ ret = asoc_simple_parse_tdm(np, dai);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int simple_link_init(struct asoc_simple_priv *priv,
+ struct device_node *node,
+ struct device_node *codec,
+ struct link_info *li,
+ char *prefix, char *name)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ int ret;
+
+ ret = asoc_simple_parse_daifmt(dev, node, codec,
+ prefix, &dai_link->dai_fmt);
+ if (ret < 0)
+ return 0;
+
+ dai_link->init = asoc_simple_dai_init;
+ dai_link->ops = &simple_ops;
+
+ return asoc_simple_set_dailink_name(dev, dai_link, name);
+}
+
static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
@@ -121,24 +179,21 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct device *dev = simple_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
- struct asoc_simple_dai *dai;
- struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
- struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
- struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
struct device_node *top = dev->of_node;
struct device_node *node = of_get_parent(np);
char *prefix = "";
+ char dai_name[64];
int ret;
dev_dbg(dev, "link_of DPCM (%pOF)\n", np);
- li->link++;
-
/* For single DAI link & old style of DT node */
if (is_top)
prefix = PREFIX;
if (li->cpu) {
+ struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
+ struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
int is_single_links = 0;
/* Codec is dummy */
@@ -147,25 +202,16 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
- dai = simple_props_to_dai_cpu(dai_props, 0);
-
- ret = asoc_simple_parse_dai(np, cpus, &is_single_links);
- if (ret)
- goto out_put_node;
-
- ret = asoc_simple_parse_clk(dev, np, dai, cpus);
+ ret = simple_parse_node(priv, np, li, prefix, &is_single_links);
if (ret < 0)
goto out_put_node;
- ret = asoc_simple_set_dailink_name(dev, dai_link,
- "fe.%s",
- cpus->dai_name);
- if (ret < 0)
- goto out_put_node;
+ snprintf(dai_name, sizeof(dai_name), "fe.%s", cpus->dai_name);
asoc_simple_canonicalize_cpu(cpus, is_single_links);
asoc_simple_canonicalize_platform(platforms, cpus);
} else {
+ struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
struct snd_soc_codec_conf *cconf;
/* CPU is dummy */
@@ -174,22 +220,13 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
- dai = simple_props_to_dai_codec(dai_props, 0);
cconf = simple_props_to_codec_conf(dai_props, 0);
- ret = asoc_simple_parse_dai(np, codecs, NULL);
+ ret = simple_parse_node(priv, np, li, prefix, NULL);
if (ret < 0)
goto out_put_node;
- ret = asoc_simple_parse_clk(dev, np, dai, codecs);
- if (ret < 0)
- goto out_put_node;
-
- ret = asoc_simple_set_dailink_name(dev, dai_link,
- "be.%s",
- codecs->dai_name);
- if (ret < 0)
- goto out_put_node;
+ snprintf(dai_name, sizeof(dai_name), "be.%s", codecs->dai_name);
/* check "prefix" from top node */
snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
@@ -201,23 +238,14 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
}
simple_parse_convert(dev, np, &dai_props->adata);
- simple_parse_mclk_fs(top, np, codec, dai_props, prefix);
-
- ret = asoc_simple_parse_tdm(np, dai);
- if (ret)
- goto out_put_node;
-
- ret = asoc_simple_parse_daifmt(dev, node, codec,
- prefix, &dai_link->dai_fmt);
- if (ret < 0)
- goto out_put_node;
snd_soc_dai_link_set_capabilities(dai_link);
- dai_link->ops = &simple_ops;
- dai_link->init = asoc_simple_dai_init;
+ ret = simple_link_init(priv, node, codec, li, prefix, dai_name);
out_put_node:
+ li->link++;
+
of_node_put(node);
return ret;
}
@@ -230,23 +258,19 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv,
{
struct device *dev = simple_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
- struct asoc_simple_dai *cpu_dai = simple_props_to_dai_cpu(dai_props, 0);
- struct asoc_simple_dai *codec_dai = simple_props_to_dai_codec(dai_props, 0);
struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
- struct device_node *top = dev->of_node;
struct device_node *cpu = NULL;
struct device_node *node = NULL;
struct device_node *plat = NULL;
+ char dai_name[64];
char prop[128];
char *prefix = "";
int ret, single_cpu = 0;
cpu = np;
node = of_get_parent(np);
- li->link++;
dev_dbg(dev, "link_of (%pOF)\n", node);
@@ -257,18 +281,11 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv,
snprintf(prop, sizeof(prop), "%splat", prefix);
plat = of_get_child_by_name(node, prop);
- ret = asoc_simple_parse_daifmt(dev, node, codec,
- prefix, &dai_link->dai_fmt);
- if (ret < 0)
- goto dai_link_of_err;
-
- simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix);
-
- ret = asoc_simple_parse_dai(cpu, cpus, &single_cpu);
+ ret = simple_parse_node(priv, cpu, li, prefix, &single_cpu);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_parse_dai(codec, codecs, NULL);
+ ret = simple_parse_node(priv, codec, li, prefix, NULL);
if (ret < 0)
goto dai_link_of_err;
@@ -276,39 +293,20 @@ static int simple_dai_link_of(struct asoc_simple_priv *priv,
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_parse_tdm(cpu, cpu_dai);
- if (ret < 0)
- goto dai_link_of_err;
-
- ret = asoc_simple_parse_tdm(codec, codec_dai);
- if (ret < 0)
- goto dai_link_of_err;
-
- ret = asoc_simple_parse_clk(dev, cpu, cpu_dai, cpus);
- if (ret < 0)
- goto dai_link_of_err;
-
- ret = asoc_simple_parse_clk(dev, codec, codec_dai, codecs);
- if (ret < 0)
- goto dai_link_of_err;
-
- ret = asoc_simple_set_dailink_name(dev, dai_link,
- "%s-%s",
- cpus->dai_name,
- codecs->dai_name);
- if (ret < 0)
- goto dai_link_of_err;
-
- dai_link->ops = &simple_ops;
- dai_link->init = asoc_simple_dai_init;
+ snprintf(dai_name, sizeof(dai_name),
+ "%s-%s", cpus->dai_name, codecs->dai_name);
asoc_simple_canonicalize_cpu(cpus, single_cpu);
asoc_simple_canonicalize_platform(platforms, cpus);
+ ret = simple_link_init(priv, node, codec, li, prefix, dai_name);
+
dai_link_of_err:
of_node_put(plat);
of_node_put(node);
+ li->link++;
+
return ret;
}
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
index 907f5f1f7b44..ff05b9779e4b 100644
--- a/sound/soc/hisilicon/hi6210-i2s.c
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -102,18 +102,15 @@ static int hi6210_i2s_startup(struct snd_pcm_substream *substream,
for (n = 0; n < i2s->clocks; n++) {
ret = clk_prepare_enable(i2s->clk[n]);
- if (ret) {
- while (n--)
- clk_disable_unprepare(i2s->clk[n]);
- return ret;
- }
+ if (ret)
+ goto err_unprepare_clk;
}
ret = clk_set_rate(i2s->clk[CLK_I2S_BASE], 49152000);
if (ret) {
dev_err(i2s->dev, "%s: setting 49.152MHz base rate failed %d\n",
__func__, ret);
- return ret;
+ goto err_unprepare_clk;
}
/* enable clock before frequency division */
@@ -165,6 +162,11 @@ static int hi6210_i2s_startup(struct snd_pcm_substream *substream,
hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
return 0;
+
+err_unprepare_clk:
+ while (n--)
+ clk_disable_unprepare(i2s->clk[n]);
+ return ret;
}
static void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index 0843235d73c9..fd3432a1d6ab 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -464,7 +464,7 @@ static int img_i2s_in_probe(struct platform_device *pdev)
if (ret)
goto err_pm_disable;
}
- ret = pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
goto err_suspend;
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 58379393b8e4..eef5f4ac87c5 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -26,6 +26,12 @@ config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES
interface.
If unsure select N.
+config SND_SOC_INTEL_HDA_DSP_COMMON
+ tristate
+
+config SND_SOC_INTEL_SOF_MAXIM_COMMON
+ tristate
+
if SND_SOC_INTEL_CATPT
config SND_SOC_INTEL_HASWELL_MACH
@@ -278,6 +284,7 @@ config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
select SND_SOC_MAX98390
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
config SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON
tristate
@@ -304,6 +311,7 @@ config SND_SOC_INTEL_BXT_RT298_MACH
select SND_SOC_RT298
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
@@ -422,6 +430,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
select SND_SOC_MAX98357A
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
help
This adds support for ASoC machine driver for Geminilake platforms
with RT5682 + MAX98357A I2S audio codec.
@@ -437,6 +446,7 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH
depends on SND_HDA_CODEC_HDMI
depends on GPIOLIB
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_DMIC
# SND_SOC_HDAC_HDA is already selected
help
@@ -461,18 +471,38 @@ config SND_SOC_INTEL_SOF_RT5682_MACH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
help
This adds support for ASoC machine driver for SOF platforms
with rt5682 codec.
Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_INTEL_SOF_CS42L42_MACH
+ tristate "SOF with cs42l42 codec in I2S Mode"
+ depends on I2C && ACPI
+ depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\
+ (MFD_INTEL_LPSS || COMPILE_TEST))
+ select SND_SOC_CS42L42
+ select SND_SOC_MAX98357A
+ select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
+ help
+ This adds support for ASoC machine driver for SOF platforms
+ with cs42l42 codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
config SND_SOC_INTEL_SOF_PCM512x_MACH
tristate "SOF with TI PCM512x codec"
depends on I2C && ACPI
depends on (SND_SOC_SOF_HDA_AUDIO_CODEC && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\
(SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST))
depends on SND_HDA_CODEC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_PCM512x_I2C
help
This adds support for ASoC machine driver for SOF platforms
@@ -504,6 +534,7 @@ config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH
select SND_SOC_RT5682_I2C
select SND_SOC_DMIC
select SND_SOC_HDAC_HDMI
+ select SND_SOC_INTEL_HDA_DSP_COMMON
help
This adds support for ASoC machine driver for SOF platform with
RT1011 + RT5682 I2S codec.
@@ -519,6 +550,7 @@ config SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH
depends on I2C && ACPI && GPIOLIB
depends on MFD_INTEL_LPSS || COMPILE_TEST
depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
select SND_SOC_DA7219
select SND_SOC_MAX98373_I2C
select SND_SOC_DMIC
@@ -539,6 +571,7 @@ config SND_SOC_INTEL_EHL_RT5660_MACH
depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC
select SND_SOC_RT5660
select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
help
This adds support for ASoC machine driver for Elkhart Lake
platform with RT5660 I2S audio codec.
@@ -566,6 +599,8 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT5682_SDW
select SND_SOC_DMIC
+ select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_INTEL_SOF_MAXIM_COMMON
help
Add support for Intel SoundWire-based platforms connected to
MAX98373, RT700, RT711, RT1308 and RT715
@@ -573,5 +608,4 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
endif
-
endif ## SND_SOC_INTEL_MACH
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 616c5fbab7d5..ed21b82a4cf6 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -3,11 +3,11 @@ snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-bdw-rt5650-mach-objs := bdw-rt5650.o
snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o
snd-soc-sst-broadwell-objs := broadwell.o
-snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o hda_dsp_common.o
-snd-soc-sst-bxt-rt298-objs := bxt_rt298.o hda_dsp_common.o
-snd-soc-sst-sof-pcm512x-objs := sof_pcm512x.o hda_dsp_common.o
+snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
+snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
+snd-soc-sst-sof-pcm512x-objs := sof_pcm512x.o
snd-soc-sst-sof-wm8804-objs := sof_wm8804.o
-snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o hda_dsp_common.o
+snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
snd-soc-sst-bytcr-wm5102-objs := bytcr_wm5102.o
@@ -19,28 +19,29 @@ snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o
snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o
snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
-snd-soc-sof_rt5682-objs := sof_rt5682.o hda_dsp_common.o sof_maxim_common.o sof_realtek_common.o
-snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o hda_dsp_common.o
+snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o
+snd-soc-sof_cs42l42-objs := sof_cs42l42.o
+snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o
snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o
snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o
snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o
snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o
snd-soc-kbl_rt5660-objs := kbl_rt5660.o
snd-soc-skl_rt286-objs := skl_rt286.o
-snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o
+snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o
snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
-snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o
-snd-soc-ehl-rt5660-objs := ehl_rt5660.o hda_dsp_common.o
+snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o
+snd-soc-ehl-rt5660-objs := ehl_rt5660.o
snd-soc-sof-sdw-objs += sof_sdw.o \
sof_sdw_max98373.o \
sof_sdw_rt1308.o sof_sdw_rt1316.o \
sof_sdw_rt5682.o sof_sdw_rt700.o \
sof_sdw_rt711.o sof_sdw_rt711_sdca.o \
sof_sdw_rt715.o sof_sdw_rt715_sdca.o \
- sof_maxim_common.o \
- sof_sdw_dmic.o sof_sdw_hdmi.o hda_dsp_common.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
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
@@ -74,3 +75,10 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o
obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o
obj-$(CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH) += snd-soc-ehl-rt5660.o
obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o
+
+# common modules
+snd-soc-intel-hda-dsp-common-objs := hda_dsp_common.o
+obj-$(CONFIG_SND_SOC_INTEL_HDA_DSP_COMMON) += snd-soc-intel-hda-dsp-common.o
+
+snd-soc-intel-sof-maxim-common-objs += sof_maxim_common.o
+obj-$(CONFIG_SND_SOC_INTEL_SOF_MAXIM_COMMON) += snd-soc-intel-sof-maxim-common.o
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 9ffef396f8f2..8bc95e31e3af 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -840,9 +840,9 @@ static int broxton_audio_probe(struct platform_device *pdev)
}
static const struct platform_device_id bxt_board_ids[] = {
- { .name = "bxt_da7219_max98357a" },
- { .name = "glk_da7219_max98357a" },
- { .name = "cml_da7219_max98357a" },
+ { .name = "bxt_da7219_mx98357a" },
+ { .name = "glk_da7219_mx98357a" },
+ { .name = "cml_da7219_mx98357a" },
{ }
};
@@ -866,6 +866,7 @@ MODULE_AUTHOR("Naveen Manohar <[email protected]>");
MODULE_AUTHOR("Mac Chiang <[email protected]>");
MODULE_AUTHOR("Brent Lu <[email protected]>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:bxt_da7219_max98357a");
-MODULE_ALIAS("platform:glk_da7219_max98357a");
-MODULE_ALIAS("platform:cml_da7219_max98357a");
+MODULE_ALIAS("platform:bxt_da7219_mx98357a");
+MODULE_ALIAS("platform:glk_da7219_mx98357a");
+MODULE_ALIAS("platform:cml_da7219_mx98357a");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 0f3157dfa838..32a776fa0b86 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -667,3 +667,4 @@ MODULE_DESCRIPTION("Intel SST Audio for Broxton");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bxt_alc298s_i2s");
MODULE_ALIAS("platform:glk_alc298s_i2s");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 2bfe3e4c696f..a9e51bbf018c 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -198,7 +198,6 @@ static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = {
| SND_SOC_DAIFMT_CBS_CFS,
.init = byt_cht_cx2072x_init,
.be_hw_params_fixup = byt_cht_cx2072x_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(ssp2, cx2072x, platform),
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
index cfeba27252ba..a28773fb7892 100644
--- a/sound/soc/intel/boards/bytcht_da7213.c
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -197,7 +197,6 @@ static struct snd_soc_dai_link dailink[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ops = &ssp2_ops,
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 06df2d46d910..a0af91580184 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -337,7 +337,6 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = byt_cht_es8316_codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_cht_es8316_init,
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
index 8c0dab1f4030..9b48fe701a2c 100644
--- a/sound/soc/intel/boards/bytcht_nocodec.c
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -144,7 +144,6 @@ static struct snd_soc_dai_link dais[] = {
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = codec_fixup,
.ignore_suspend = 1,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(ssp2_port, dummy, platform),
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index df2f5d55e8ff..91a6d712eb58 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -574,6 +574,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1 |
BYT_RT5640_MCLK_EN),
},
+ { /* Glavey TM800A550L */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS version */
+ DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
+ },
+ .driver_data = (void *)(BYTCR_INPUT_DEFAULTS |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
@@ -652,6 +663,20 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_MONO_SPEAKER |
BYT_RT5640_MCLK_EN),
},
+ { /* Lenovo Miix 3-830 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 3-830"),
+ },
+ .driver_data = (void *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_JD_SRC_JD2_IN4N |
+ BYT_RT5640_OVCD_TH_2000UA |
+ BYT_RT5640_OVCD_SF_0P75 |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF1 |
+ BYT_RT5640_MCLK_EN),
+ },
{ /* Linx Linx7 tablet */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LINX"),
@@ -1180,7 +1205,6 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = byt_rt5640_codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_rt5640_init,
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 148b7b1bd3e8..e13c0c63a949 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -786,7 +786,6 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = byt_rt5651_codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_rt5651_init,
diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
index 8d8ab9be256f..580d5fddae5a 100644
--- a/sound/soc/intel/boards/bytcr_wm5102.c
+++ b/sound/soc/intel/boards/bytcr_wm5102.c
@@ -351,7 +351,6 @@ static struct snd_soc_dai_link byt_wm5102_dais[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = byt_wm5102_codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_wm5102_init,
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 6fea554cfed5..804dbc7911d5 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -471,7 +471,6 @@ static struct snd_soc_dai_link cht_dailink[] = {
.no_pcm = 1,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ops = &cht_be_ssp2_ops,
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index e358632f50d7..9509b6e161b8 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -375,7 +375,6 @@ static struct snd_soc_dai_link cht_dailink[] = {
.name = "SSP2-Codec",
.id = 0,
.no_pcm = 1,
- .nonatomic = true,
.init = cht_codec_init,
.be_hw_params_fixup = cht_codec_fixup,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c
index 14813beb33d1..27615acddacd 100644
--- a/sound/soc/intel/boards/cml_rt1011_rt5682.c
+++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c
@@ -594,3 +594,4 @@ MODULE_AUTHOR("Shuming Fan <[email protected]>");
MODULE_AUTHOR("Mac Chiang <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:cml_rt1011_rt5682");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c
index 7c0d4e915406..00773d17d578 100644
--- a/sound/soc/intel/boards/ehl_rt5660.c
+++ b/sound/soc/intel/boards/ehl_rt5660.c
@@ -181,7 +181,6 @@ static struct snd_soc_dai_link ehl_rt5660_dailink[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
.ops = &rt5660_ops,
- .nonatomic = true,
SND_SOC_DAILINK_REG(ssp0_pin, rt5660_codec, platform),
},
{
@@ -321,3 +320,4 @@ MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");
MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:ehl_rt5660");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c
index 62cca511522e..19e2ff90886a 100644
--- a/sound/soc/intel/boards/glk_rt5682_max98357a.c
+++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c
@@ -642,3 +642,4 @@ MODULE_AUTHOR("Naveen Manohar <[email protected]>");
MODULE_AUTHOR("Harsha Priya <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:glk_rt5682_max98357a");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c
index 91ad2a0ad1ce..efdc4bc4bb1f 100644
--- a/sound/soc/intel/boards/hda_dsp_common.c
+++ b/sound/soc/intel/boards/hda_dsp_common.c
@@ -2,6 +2,7 @@
//
// Copyright(c) 2019 Intel Corporation. All rights reserved.
+#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/hda_codec.h>
@@ -82,5 +83,9 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card,
return err;
}
+EXPORT_SYMBOL_NS(hda_dsp_hdmi_build_controls, SND_SOC_INTEL_HDA_DSP_COMMON);
#endif
+
+MODULE_DESCRIPTION("ASoC Intel HDMI helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
index bc50eda297ab..f4b4eeca3e03 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_generic.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -258,3 +258,4 @@ MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver");
MODULE_AUTHOR("Rakesh Ughreja <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl_hda_dsp_generic");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
new file mode 100644
index 000000000000..8919d3ba3c89
--- /dev/null
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2021 Intel Corporation.
+
+/*
+ * Intel SOF Machine Driver with Cirrus Logic CS42L42 Codec
+ * and speaker codec MAX98357A
+ */
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dmi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <dt-bindings/sound/cs42l42.h>
+#include "../../codecs/hdac_hdmi.h"
+#include "../common/soc-intel-quirks.h"
+#include "hda_dsp_common.h"
+#include "sof_maxim_common.h"
+
+#define NAME_SIZE 32
+
+#define SOF_CS42L42_SSP_CODEC(quirk) ((quirk) & GENMASK(2, 0))
+#define SOF_CS42L42_SSP_CODEC_MASK (GENMASK(2, 0))
+#define SOF_SPEAKER_AMP_PRESENT BIT(3)
+#define SOF_CS42L42_SSP_AMP_SHIFT 4
+#define SOF_CS42L42_SSP_AMP_MASK (GENMASK(6, 4))
+#define SOF_CS42L42_SSP_AMP(quirk) \
+ (((quirk) << SOF_CS42L42_SSP_AMP_SHIFT) & SOF_CS42L42_SSP_AMP_MASK)
+#define SOF_CS42L42_NUM_HDMIDEV_SHIFT 7
+#define SOF_CS42L42_NUM_HDMIDEV_MASK (GENMASK(9, 7))
+#define SOF_CS42L42_NUM_HDMIDEV(quirk) \
+ (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK)
+#define SOF_MAX98357A_SPEAKER_AMP_PRESENT BIT(10)
+
+/* Default: SSP2 */
+static unsigned long sof_cs42l42_quirk = SOF_CS42L42_SSP_CODEC(2);
+
+struct sof_hdmi_pcm {
+ struct list_head head;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_jack hdmi_jack;
+ int device;
+};
+
+struct sof_card_private {
+ struct snd_soc_jack headset_jack;
+ struct list_head hdmi_pcm_list;
+ bool common_hdmi_codec_drv;
+};
+
+static int sof_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
+ struct sof_hdmi_pcm *pcm;
+
+ pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+
+ /* dai_link id is 1:1 mapped to the PCM device */
+ pcm->device = rtd->dai_link->id;
+ pcm->codec_dai = dai;
+
+ list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+ return 0;
+}
+
+static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+ struct snd_soc_jack *jack = &ctx->headset_jack;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack, NULL, 0);
+ 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_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ 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 ret;
+};
+
+static void sof_cs42l42_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int sof_cs42l42_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ int clk_freq, ret;
+
+ clk_freq = 3072000; /* BCLK freq */
+
+ /* Configure sysclk for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ clk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ return ret;
+}
+
+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)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component = NULL;
+ char jack_name[NAME_SIZE];
+ struct sof_hdmi_pcm *pcm;
+ int err;
+
+ if (list_empty(&ctx->hdmi_pcm_list))
+ return -EINVAL;
+
+ if (ctx->common_hdmi_codec_drv) {
+ pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm,
+ head);
+ component = pcm->codec_dai->component;
+ return hda_dsp_hdmi_build_controls(card, component);
+ }
+
+ list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+ component = pcm->codec_dai->component;
+ snprintf(jack_name, sizeof(jack_name),
+ "HDMI/DP, pcm=%d Jack", pcm->device);
+ err = snd_soc_card_jack_new(card, jack_name,
+ SND_JACK_AVOUT, &pcm->hdmi_jack,
+ NULL, 0);
+
+ if (err)
+ return err;
+
+ err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+ &pcm->hdmi_jack);
+ if (err < 0)
+ return err;
+ }
+
+ return hdac_hdmi_jack_port_init(component, &card->dapm);
+}
+
+static const struct snd_kcontrol_new sof_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget sof_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_widget dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route sof_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone Jack", NULL, "HP"},
+
+ /* other jacks */
+ {"HS", NULL, "Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route dmic_map[] = {
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+};
+
+static int dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
+ ARRAY_SIZE(dmic_widgets));
+ if (ret) {
+ dev_err(card->dev, "DMic widget addition failed: %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, dmic_map,
+ ARRAY_SIZE(dmic_map));
+
+ if (ret)
+ dev_err(card->dev, "DMic map addition failed: %d\n", ret);
+
+ return ret;
+}
+
+/* sof audio machine driver for cs42l42 codec */
+static struct snd_soc_card sof_audio_card_cs42l42 = {
+ .name = "cs42l42", /* the sof- prefix is added by the core */
+ .owner = THIS_MODULE,
+ .controls = sof_controls,
+ .num_controls = ARRAY_SIZE(sof_controls),
+ .dapm_widgets = sof_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sof_widgets),
+ .dapm_routes = sof_map,
+ .num_dapm_routes = ARRAY_SIZE(sof_map),
+ .fully_routed = true,
+ .late_probe = sof_card_late_probe,
+};
+
+static struct snd_soc_dai_link_component cs42l42_component[] = {
+ {
+ .name = "i2c-10134242:00",
+ .dai_name = "cs42l42",
+ }
+};
+
+static struct snd_soc_dai_link_component dmic_component[] = {
+ {
+ .name = "dmic-codec",
+ .dai_name = "dmic-hifi",
+ }
+};
+
+static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
+ int ssp_codec,
+ int ssp_amp,
+ int dmic_be_num,
+ int hdmi_num)
+{
+ struct snd_soc_dai_link_component *idisp_components;
+ struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link *links;
+ int i, id = 0;
+
+ links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
+ sof_audio_card_cs42l42.num_links, GFP_KERNEL);
+ cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component) *
+ sof_audio_card_cs42l42.num_links, GFP_KERNEL);
+ if (!links || !cpus)
+ goto devm_err;
+
+ /* speaker amp */
+ if (sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d-Codec", ssp_amp);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+
+ if (sof_cs42l42_quirk & SOF_MAX98357A_SPEAKER_AMP_PRESENT) {
+ max_98357a_dai_link(&links[id]);
+ } else {
+ dev_err(dev, "no amp defined\n");
+ goto devm_err;
+ }
+
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].dpcm_playback = 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++;
+ }
+
+ /* 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++;
+
+ /* dmic */
+ if (dmic_be_num > 0) {
+ /* at least we have dmic01 */
+ links[id].name = "dmic01";
+ links[id].cpus = &cpus[id];
+ links[id].cpus->dai_name = "DMIC01 Pin";
+ links[id].init = dmic_init;
+ if (dmic_be_num > 1) {
+ /* set up 2 BE links at most */
+ links[id + 1].name = "dmic16k";
+ links[id + 1].cpus = &cpus[id + 1];
+ links[id + 1].cpus->dai_name = "DMIC16k Pin";
+ dmic_be_num = 2;
+ }
+ }
+
+ for (i = 0; i < dmic_be_num; i++) {
+ links[id].id = id;
+ links[id].num_cpus = 1;
+ links[id].codecs = dmic_component;
+ links[id].num_codecs = ARRAY_SIZE(dmic_component);
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].ignore_suspend = 1;
+ links[id].dpcm_capture = 1;
+ links[id].no_pcm = 1;
+ id++;
+ }
+
+ /* HDMI */
+ if (hdmi_num > 0) {
+ idisp_components = devm_kzalloc(dev,
+ sizeof(struct snd_soc_dai_link_component) *
+ hdmi_num, GFP_KERNEL);
+ if (!idisp_components)
+ goto devm_err;
+ }
+ for (i = 1; i <= hdmi_num; i++) {
+ links[id].name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d", i);
+ if (!links[id].name)
+ goto devm_err;
+
+ links[id].id = id;
+ links[id].cpus = &cpus[id];
+ links[id].num_cpus = 1;
+ links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "iDisp%d Pin", i);
+ if (!links[id].cpus->dai_name)
+ goto devm_err;
+
+ idisp_components[i - 1].name = "ehdaudio0D2";
+ idisp_components[i - 1].dai_name = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "intel-hdmi-hifi%d",
+ i);
+ if (!idisp_components[i - 1].dai_name)
+ goto devm_err;
+
+ links[id].codecs = &idisp_components[i - 1];
+ links[id].num_codecs = 1;
+ links[id].platforms = platform_component;
+ links[id].num_platforms = ARRAY_SIZE(platform_component);
+ links[id].init = sof_hdmi_init;
+ links[id].dpcm_playback = 1;
+ links[id].no_pcm = 1;
+ id++;
+ }
+
+ return links;
+devm_err:
+ return NULL;
+}
+
+static int sof_audio_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_links;
+ struct snd_soc_acpi_mach *mach;
+ struct sof_card_private *ctx;
+ int dmic_be_num, hdmi_num;
+ int ret, ssp_amp, ssp_codec;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (pdev->id_entry && pdev->id_entry->driver_data)
+ sof_cs42l42_quirk = (unsigned long)pdev->id_entry->driver_data;
+
+ mach = pdev->dev.platform_data;
+
+ if (soc_intel_is_glk()) {
+ dmic_be_num = 1;
+ hdmi_num = 3;
+ } else {
+ dmic_be_num = 2;
+ hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >>
+ SOF_CS42L42_NUM_HDMIDEV_SHIFT;
+ /* default number of HDMI DAI's */
+ if (!hdmi_num)
+ hdmi_num = 3;
+ }
+
+ dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
+
+ 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;
+
+ /* compute number of dai links */
+ sof_audio_card_cs42l42.num_links = 1 + dmic_be_num + hdmi_num;
+
+ if (sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT)
+ sof_audio_card_cs42l42.num_links++;
+
+ dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp,
+ dmic_be_num, hdmi_num);
+ if (!dai_links)
+ return -ENOMEM;
+
+ sof_audio_card_cs42l42.dai_link = dai_links;
+
+ INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
+ sof_audio_card_cs42l42.dev = &pdev->dev;
+
+ /* set platform name for each dailink */
+ ret = snd_soc_fixup_dai_links_platform_name(&sof_audio_card_cs42l42,
+ mach->mach_params.platform);
+ if (ret)
+ return ret;
+
+ ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
+
+ snd_soc_card_set_drvdata(&sof_audio_card_cs42l42, ctx);
+
+ return devm_snd_soc_register_card(&pdev->dev,
+ &sof_audio_card_cs42l42);
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "glk_cs4242_mx98357a",
+ .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98357A_SPEAKER_AMP_PRESENT |
+ SOF_CS42L42_SSP_AMP(1)),
+ },
+ { }
+};
+
+static struct platform_driver sof_audio = {
+ .probe = sof_audio_probe,
+ .driver = {
+ .name = "sof_cs42l42",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = board_ids,
+};
+module_platform_driver(sof_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("SOF Audio Machine driver for CS42L42");
+MODULE_AUTHOR("Brent Lu <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sof_cs42l42");
+MODULE_ALIAS("platform:glk_cs4242_max98357a");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c
index f3cb0773e70e..2116d70d1ea8 100644
--- a/sound/soc/intel/boards/sof_da7219_max98373.c
+++ b/sound/soc/intel/boards/sof_da7219_max98373.c
@@ -440,6 +440,7 @@ static const struct platform_device_id board_ids[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(platform, board_ids);
static struct platform_driver audio = {
.probe = audio_probe,
@@ -457,3 +458,4 @@ MODULE_AUTHOR("Yong Zhi <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sof_da7219_max98360a");
MODULE_ALIAS("platform:sof_da7219_max98373");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c
index 437d20562753..e9c52f8b6428 100644
--- a/sound/soc/intel/boards/sof_maxim_common.c
+++ b/sound/soc/intel/boards/sof_maxim_common.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2020 Intel Corporation. All rights reserved.
+#include <linux/module.h>
#include <linux/string.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -16,6 +17,7 @@ const struct snd_soc_dapm_route max_98373_dapm_routes[] = {
{ "Left Spk", NULL, "Left BE_OUT" },
{ "Right Spk", NULL, "Right BE_OUT" },
};
+EXPORT_SYMBOL_NS(max_98373_dapm_routes, SND_SOC_INTEL_SOF_MAXIM_COMMON);
static struct snd_soc_codec_conf max_98373_codec_conf[] = {
{
@@ -38,9 +40,10 @@ struct snd_soc_dai_link_component max_98373_components[] = {
.dai_name = MAX_98373_CODEC_DAI,
},
};
+EXPORT_SYMBOL_NS(max_98373_components, SND_SOC_INTEL_SOF_MAXIM_COMMON);
-static int max98373_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int max_98373_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
@@ -59,7 +62,7 @@ static int max98373_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-int max98373_trigger(struct snd_pcm_substream *substream, int cmd)
+int max_98373_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
@@ -102,13 +105,15 @@ int max98373_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
+EXPORT_SYMBOL_NS(max_98373_trigger, SND_SOC_INTEL_SOF_MAXIM_COMMON);
struct snd_soc_ops max_98373_ops = {
- .hw_params = max98373_hw_params,
- .trigger = max98373_trigger,
+ .hw_params = max_98373_hw_params,
+ .trigger = max_98373_trigger,
};
+EXPORT_SYMBOL_NS(max_98373_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON);
-int max98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
+int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
int ret;
@@ -119,9 +124,74 @@ int max98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd)
dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret);
return ret;
}
+EXPORT_SYMBOL_NS(max_98373_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON);
-void sof_max98373_codec_conf(struct snd_soc_card *card)
+void max_98373_set_codec_conf(struct snd_soc_card *card)
{
card->codec_conf = max_98373_codec_conf;
card->num_configs = ARRAY_SIZE(max_98373_codec_conf);
}
+EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+/*
+ * Maxim MAX98357A
+ */
+static const struct snd_kcontrol_new max_98357a_kcontrols[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget max_98357a_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max_98357a_dapm_routes[] = {
+ /* speaker */
+ {"Spk", NULL, "Speaker"},
+};
+
+static struct snd_soc_dai_link_component max_98357a_components[] = {
+ {
+ .name = MAX_98357A_DEV0_NAME,
+ .dai_name = MAX_98357A_CODEC_DAI,
+ }
+};
+
+static int max_98357a_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max_98357a_dapm_widgets,
+ ARRAY_SIZE(max_98357a_dapm_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max_98357a_kcontrols,
+ ARRAY_SIZE(max_98357a_kcontrols));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, max_98357a_dapm_routes,
+ ARRAY_SIZE(max_98357a_dapm_routes));
+
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
+}
+
+void max_98357a_dai_link(struct snd_soc_dai_link *link)
+{
+ link->codecs = max_98357a_components;
+ link->num_codecs = ARRAY_SIZE(max_98357a_components);
+ link->init = max_98357a_init;
+}
+EXPORT_SYMBOL_NS(max_98357a_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON);
+
+MODULE_DESCRIPTION("ASoC Intel SOF Maxim helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h
index 5240b1c9d379..2674f1e373ef 100644
--- a/sound/soc/intel/boards/sof_maxim_common.h
+++ b/sound/soc/intel/boards/sof_maxim_common.h
@@ -20,8 +20,16 @@ extern struct snd_soc_dai_link_component max_98373_components[2];
extern struct snd_soc_ops max_98373_ops;
extern const struct snd_soc_dapm_route max_98373_dapm_routes[];
-int max98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd);
-void sof_max98373_codec_conf(struct snd_soc_card *card);
-int max98373_trigger(struct snd_pcm_substream *substream, int cmd);
+int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd);
+void max_98373_set_codec_conf(struct snd_soc_card *card);
+int max_98373_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/*
+ * Maxim MAX98357A
+ */
+#define MAX_98357A_CODEC_DAI "HiFi"
+#define MAX_98357A_DEV0_NAME "MX98357A:00"
+
+void max_98357a_dai_link(struct snd_soc_dai_link *link);
#endif /* __SOF_MAXIM_COMMON_H */
diff --git a/sound/soc/intel/boards/sof_pcm512x.c b/sound/soc/intel/boards/sof_pcm512x.c
index d2b0456236c7..2ec9c62366e2 100644
--- a/sound/soc/intel/boards/sof_pcm512x.c
+++ b/sound/soc/intel/boards/sof_pcm512x.c
@@ -241,7 +241,6 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].init = sof_pcm512x_codec_init;
links[id].ops = &sof_pcm512x_ops;
- links[id].nonatomic = true;
links[id].dpcm_playback = 1;
/*
* capture only supported with specific versions of the Hifiberry DAC+
@@ -437,3 +436,4 @@ MODULE_DESCRIPTION("ASoC Intel(R) SOF + PCM512x Machine driver");
MODULE_AUTHOR("Pierre-Louis Bossart");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sof_pcm512x");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index 58548ea0d915..3e69feaf052b 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -50,6 +50,13 @@
#define SOF_MAX98373_SPEAKER_AMP_PRESENT BIT(17)
#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(18)
+/* BT audio offload: reserve 3 bits for future */
+#define SOF_BT_OFFLOAD_SSP_SHIFT 19
+#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(21, 19))
+#define SOF_BT_OFFLOAD_SSP(quirk) \
+ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
+#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22)
+
/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
SOF_RT5682_SSP_CODEC(0);
@@ -444,20 +451,26 @@ static int sof_card_late_probe(struct snd_soc_card *card)
static const struct snd_kcontrol_new sof_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Spk"),
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
+static const struct snd_kcontrol_new speaker_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
static const struct snd_soc_dapm_widget sof_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
+static const struct snd_soc_dapm_widget speaker_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
static const struct snd_soc_dapm_widget dmic_widgets[] = {
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
};
@@ -497,6 +510,21 @@ static int speaker_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = rtd->card;
int ret;
+ ret = snd_soc_dapm_new_controls(&card->dapm, speaker_widgets,
+ ARRAY_SIZE(speaker_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, speaker_controls,
+ ARRAY_SIZE(speaker_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map,
ARRAY_SIZE(speaker_map));
@@ -566,13 +594,6 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
-static struct snd_soc_dai_link_component max98357a_component[] = {
- {
- .name = "MX98357A:00",
- .dai_name = "HiFi",
- }
-};
-
static struct snd_soc_dai_link_component max98360a_component[] = {
{
.name = "MX98360A:00",
@@ -591,6 +612,13 @@ static struct snd_soc_dai_link_component rt1015_components[] = {
},
};
+static struct snd_soc_dai_link_component dummy_component[] = {
+ {
+ .name = "snd-soc-dummy",
+ .dai_name = "snd-soc-dummy-dai",
+ }
+};
+
static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int ssp_codec,
int ssp_amp,
@@ -623,7 +651,6 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
links[id].init = sof_rt5682_codec_init;
links[id].exit = sof_rt5682_codec_exit;
links[id].ops = &sof_rt5682_ops;
- links[id].nonatomic = true;
links[id].dpcm_playback = 1;
links[id].dpcm_capture = 1;
links[id].no_pcm = 1;
@@ -742,7 +769,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
SOF_MAX98373_SPEAKER_AMP_PRESENT) {
links[id].codecs = max_98373_components;
links[id].num_codecs = ARRAY_SIZE(max_98373_components);
- links[id].init = max98373_spk_codec_init;
+ links[id].init = max_98373_spk_codec_init;
links[id].ops = &max_98373_ops;
/* feedback stream */
links[id].dpcm_capture = 1;
@@ -755,13 +782,10 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
SOF_RT1011_SPEAKER_AMP_PRESENT) {
sof_rt1011_dai_link(&links[id]);
} else {
- links[id].codecs = max98357a_component;
- links[id].num_codecs = ARRAY_SIZE(max98357a_component);
- links[id].init = speaker_codec_init;
+ max_98357a_dai_link(&links[id]);
}
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
- links[id].nonatomic = true;
links[id].dpcm_playback = 1;
links[id].no_pcm = 1;
links[id].cpus = &cpus[id];
@@ -780,6 +804,31 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
if (!links[id].cpus->dai_name)
goto devm_err;
}
+ id++;
+ }
+
+ /* 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 = dummy_component;
+ links[id].num_codecs = ARRAY_SIZE(dummy_component);
+ 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;
}
return links;
@@ -863,12 +912,15 @@ static int sof_audio_probe(struct platform_device *pdev)
sof_audio_card_rt5682.num_links++;
if (sof_rt5682_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT)
- sof_max98373_codec_conf(&sof_audio_card_rt5682);
+ max_98373_set_codec_conf(&sof_audio_card_rt5682);
else if (sof_rt5682_quirk & SOF_RT1011_SPEAKER_AMP_PRESENT)
sof_rt1011_codec_conf(&sof_audio_card_rt5682);
else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT)
sof_rt1015p_codec_conf(&sof_audio_card_rt5682);
+ if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
+ sof_audio_card_rt5682.num_links++;
+
dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp,
dmic_be_num, hdmi_num);
if (!dai_links)
@@ -909,7 +961,9 @@ static const struct platform_device_id board_ids[] = {
SOF_RT5682_SSP_CODEC(0) |
SOF_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(1) |
- SOF_RT5682_NUM_HDMIDEV(4)),
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.name = "jsl_rt5682_rt1015",
@@ -927,7 +981,9 @@ static const struct platform_device_id board_ids[] = {
SOF_SPEAKER_AMP_PRESENT |
SOF_MAX98373_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(1) |
- SOF_RT5682_NUM_HDMIDEV(4)),
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.name = "jsl_rt5682_max98360a",
@@ -955,7 +1011,9 @@ static const struct platform_device_id board_ids[] = {
SOF_SPEAKER_AMP_PRESENT |
SOF_RT1011_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(1) |
- SOF_RT5682_NUM_HDMIDEV(4)),
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.name = "jsl_rt5682_rt1015p",
@@ -966,8 +1024,28 @@ static const struct platform_device_id board_ids[] = {
SOF_RT1015P_SPEAKER_AMP_PRESENT |
SOF_RT5682_SSP_AMP(1)),
},
+ {
+ .name = "adl_max98373_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98373_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(1) |
+ SOF_RT5682_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .name = "adl_max98357a_rt5682",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_RT5682_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_RT5682_SSP_AMP(2) |
+ SOF_RT5682_NUM_HDMIDEV(4)),
+ },
{ }
};
+MODULE_DEVICE_TABLE(platform, board_ids);
static struct platform_driver sof_audio = {
.probe = sof_audio_probe,
@@ -993,3 +1071,7 @@ MODULE_ALIAS("platform:jsl_rt5682_max98360a");
MODULE_ALIAS("platform:cml_rt1015_rt5682");
MODULE_ALIAS("platform:tgl_rt1011_rt5682");
MODULE_ALIAS("platform:jsl_rt5682_rt1015p");
+MODULE_ALIAS("platform:adl_max98373_rt5682");
+MODULE_ALIAS("platform:adl_max98357a_rt5682");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index ecd3f90f4bbe..dd5d8e6af626 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -147,7 +147,9 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_SDW_TGL_HDMI |
SOF_SDW_PCH_DMIC |
- SOF_SDW_FOUR_SPK),
+ SOF_SDW_FOUR_SPK |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{
.callback = sof_sdw_quirk_cb,
@@ -196,7 +198,22 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
},
.driver_data = (void *)(SOF_RT711_JD_SRC_JD1 |
SOF_SDW_TGL_HDMI |
- SOF_SDW_PCH_DMIC),
+ SOF_RT715_DAI_ID_FIX |
+ SOF_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Brya"),
+ },
+ .driver_data = (void *)(SOF_SDW_TGL_HDMI |
+ SOF_SDW_PCH_DMIC |
+ SOF_SDW_FOUR_SPK |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
},
{}
};
@@ -353,6 +370,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.part_id = 0x714,
.version_id = 3,
.direction = {false, true},
+ .ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_sdca_init,
},
@@ -360,6 +378,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.part_id = 0x715,
.version_id = 3,
.direction = {false, true},
+ .ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_sdca_init,
},
@@ -367,6 +386,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.part_id = 0x714,
.version_id = 2,
.direction = {false, true},
+ .ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_init,
},
@@ -374,6 +394,7 @@ static struct sof_sdw_codec_info codec_info_list[] = {
.part_id = 0x715,
.version_id = 2,
.direction = {false, true},
+ .ignore_pch_dmic = true,
.dai_name = "rt715-aif2",
.init = sof_sdw_rt715_init,
},
@@ -499,7 +520,6 @@ static void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links
dai_links->name = name;
dai_links->platforms = platform_component;
dai_links->num_platforms = ARRAY_SIZE(platform_component);
- dai_links->nonatomic = true;
dai_links->no_pcm = 1;
dai_links->cpus = cpus;
dai_links->num_cpus = cpus_num;
@@ -729,7 +749,8 @@ static int create_sdw_dailink(struct device *dev, int *be_index,
int *cpu_id, bool *group_generated,
struct snd_soc_codec_conf *codec_conf,
int codec_count,
- int *codec_conf_index)
+ int *codec_conf_index,
+ bool *ignore_pch_dmic)
{
const struct snd_soc_acpi_link_adr *link_next;
struct snd_soc_dai_link_component *codecs;
@@ -782,6 +803,9 @@ static int create_sdw_dailink(struct device *dev, int *be_index,
if (codec_index < 0)
return codec_index;
+ if (codec_info_list[codec_index].ignore_pch_dmic)
+ *ignore_pch_dmic = true;
+
cpu_dai_index = *cpu_id;
for_each_pcm_streams(stream) {
char *name, *cpu_name;
@@ -913,6 +937,7 @@ static int sof_card_dai_links_create(struct device *dev,
const struct snd_soc_acpi_link_adr *adr_link;
struct snd_soc_dai_link_component *cpus;
struct snd_soc_codec_conf *codec_conf;
+ bool ignore_pch_dmic = false;
int codec_conf_count;
int codec_conf_index = 0;
bool group_generated[SDW_MAX_GROUPS];
@@ -968,6 +993,9 @@ static int sof_card_dai_links_create(struct device *dev,
dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) ? 2 : 0;
comp_num += dmic_num;
+ if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
+ comp_num++;
+
dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num,
dmic_num, ctx->idisp_codec ? hdmi_num : 0);
@@ -1019,7 +1047,8 @@ static int sof_card_dai_links_create(struct device *dev,
sdw_cpu_dai_num, cpus, adr_link,
&cpu_id, group_generated,
codec_conf, codec_conf_count,
- &codec_conf_index);
+ &codec_conf_index,
+ &ignore_pch_dmic);
if (ret < 0) {
dev_err(dev, "failed to create dai link %d", be_id);
return -ENOMEM;
@@ -1087,6 +1116,10 @@ SSP:
DMIC:
/* dmic */
if (dmic_num > 0) {
+ if (ignore_pch_dmic) {
+ dev_warn(dev, "Ignoring PCH DMIC\n");
+ goto HDMI;
+ }
cpus[cpu_id].dai_name = "DMIC01 Pin";
init_dai_link(dev, links + link_id, be_id, "dmic01",
0, 1, // DMIC only supports capture
@@ -1105,6 +1138,7 @@ DMIC:
INC_ID(be_id, cpu_id, link_id);
}
+HDMI:
/* HDMI */
if (hdmi_num > 0) {
idisp_components = devm_kcalloc(dev, hdmi_num,
@@ -1147,6 +1181,31 @@ DMIC:
INC_ID(be_id, cpu_id, link_id);
}
+ if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
+ int port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
+ SOF_BT_OFFLOAD_SSP_SHIFT;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
+ if (!name)
+ return -ENOMEM;
+
+ ssp_components = devm_kzalloc(dev, sizeof(*ssp_components),
+ GFP_KERNEL);
+ if (!ssp_components)
+ return -ENOMEM;
+
+ ssp_components->name = "snd-soc-dummy";
+ ssp_components->dai_name = "snd-soc-dummy-dai";
+
+ cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port);
+ if (!cpu_name)
+ return -ENOMEM;
+
+ cpus[cpu_id].dai_name = cpu_name;
+ init_dai_link(dev, links + link_id, be_id, name, 1, 1,
+ cpus + cpu_id, 1, ssp_components, 1, NULL, NULL);
+ }
+
card->dai_link = links;
card->num_links = num_links;
@@ -1302,3 +1361,5 @@ MODULE_AUTHOR("Rander Wang <[email protected]>");
MODULE_AUTHOR("Pierre-Louis Bossart <[email protected]>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sof_sdw");
+MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
+MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h
index f3cb6796363e..37ae3a19fa49 100644
--- a/sound/soc/intel/boards/sof_sdw_common.h
+++ b/sound/soc/intel/boards/sof_sdw_common.h
@@ -50,12 +50,20 @@ enum {
#define SOF_RT715_DAI_ID_FIX BIT(11)
#define SOF_SDW_NO_AGGREGATION BIT(12)
+/* BT audio offload: reserve 3 bits for future */
+#define SOF_BT_OFFLOAD_SSP_SHIFT 13
+#define SOF_BT_OFFLOAD_SSP_MASK (GENMASK(15, 13))
+#define SOF_BT_OFFLOAD_SSP(quirk) \
+ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK)
+#define SOF_SSP_BT_OFFLOAD_PRESENT BIT(16)
+
struct sof_sdw_codec_info {
const int part_id;
const int version_id;
int amp_num;
const u8 acpi_id[ACPI_ID_LEN];
const bool direction[2]; // playback & capture support
+ const bool ignore_pch_dmic;
const char *dai_name;
const struct snd_soc_ops *ops;
diff --git a/sound/soc/intel/boards/sof_sdw_max98373.c b/sound/soc/intel/boards/sof_sdw_max98373.c
index cfdf970c5800..0e7ed906b341 100644
--- a/sound/soc/intel/boards/sof_sdw_max98373.c
+++ b/sound/soc/intel/boards/sof_sdw_max98373.c
@@ -64,7 +64,7 @@ static int max98373_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* enable max98373 first */
- ret = max98373_trigger(substream, cmd);
+ ret = max_98373_trigger(substream, cmd);
if (ret < 0)
break;
@@ -77,7 +77,7 @@ static int max98373_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
if (ret < 0)
break;
- ret = max98373_trigger(substream, cmd);
+ ret = max_98373_trigger(substream, cmd);
break;
default:
ret = -EINVAL;
diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c
index 6a181e45143d..54395e2ededc 100644
--- a/sound/soc/intel/boards/sof_wm8804.c
+++ b/sound/soc/intel/boards/sof_wm8804.c
@@ -167,7 +167,6 @@ static struct snd_soc_dai_link dailink[] = {
.name = "SSP5-Codec",
.id = 0,
.no_pcm = 1,
- .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.ops = &sof_wm8804_ops,
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 692c4c479ed8..22c465f1d5d8 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -223,6 +223,30 @@ static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link0[] = {
{}
};
+static const struct snd_soc_acpi_adr_device mx8373_2_adr[] = {
+ {
+ .adr = 0x000223019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "Left"
+ },
+ {
+ .adr = 0x000227019F837300ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "Right"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = {
+ {
+ .adr = 0x000021025D568200ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt5682"
+ }
+};
+
static const struct snd_soc_acpi_link_adr adl_rvp[] = {
{
.mask = BIT(0),
@@ -232,7 +256,47 @@ static const struct snd_soc_acpi_link_adr adl_rvp[] = {
{}
};
+static const struct snd_soc_acpi_link_adr adl_chromebook_base[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt5682_0_adr),
+ .adr_d = rt5682_0_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(mx8373_2_adr),
+ .adr_d = mx8373_2_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_codecs adl_max98373_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98373"}
+};
+
+static const struct snd_soc_acpi_codecs adl_max98357a_amp = {
+ .num_codecs = 1,
+ .codecs = {"MX98357A"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "adl_max98373_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98373_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "adl_max98357a_rt5682",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98357a_amp,
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-max98357a-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines);
@@ -269,6 +333,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-adl-rt711.tplg",
},
+ {
+ .link_mask = 0x5, /* rt5682 on link0 & 2xmax98373 on link 2 */
+ .links = adl_chromebook_base,
+ .drv_name = "sof_sdw",
+ .sof_fw_filename = "sof-adl.ri",
+ .sof_tplg_filename = "sof-adl-sdw-max98373-rt5682.tplg",
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_sdw_machines);
diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
index 398cc771c835..576407b5daf2 100644
--- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c
@@ -56,7 +56,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = {
},
{
.id = "DLGS7219",
- .drv_name = "bxt_da7219_max98357a",
+ .drv_name = "bxt_da7219_mx98357a",
.fw_filename = "intel/dsp_fw_bxtn.bin",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &bxt_codecs,
diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
index 7f6ef8229969..459ac89f401b 100644
--- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c
@@ -67,7 +67,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = {
},
{
.id = "DLGS7219",
- .drv_name = "cml_da7219_max98357a",
+ .drv_name = "cml_da7219_mx98357a",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &max98357a_spk_codecs,
.sof_fw_filename = "sof-cml.ri",
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 6ceaab19ccb6..8c6264622da9 100644
--- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c
@@ -24,7 +24,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
},
{
.id = "DLGS7219",
- .drv_name = "glk_da7219_max98357a",
+ .drv_name = "glk_da7219_mx98357a",
.fw_filename = "intel/dsp_fw_glk.bin",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &glk_codecs,
@@ -40,6 +40,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = {
.sof_fw_filename = "sof-glk.ri",
.sof_tplg_filename = "sof-glk-rt5682.tplg",
},
+ {
+ .id = "10134242",
+ .drv_name = "glk_cs4242_mx98357a",
+ .fw_filename = "intel/dsp_fw_glk.bin",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &glk_codecs,
+ .sof_fw_filename = "sof-glk.ri",
+ .sof_tplg_filename = "sof-glk-cs42l42.tplg",
+ },
+
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines);
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 87c891c46291..64226072f0ee 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -149,8 +149,8 @@ int skl_nhlt_update_topology_bin(struct skl_dev *skl)
return 0;
}
-static ssize_t skl_nhlt_platform_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t platform_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct pci_dev *pci = to_pci_dev(dev);
struct hdac_bus *bus = pci_get_drvdata(pci);
@@ -166,7 +166,7 @@ static ssize_t skl_nhlt_platform_id_show(struct device *dev,
return sprintf(buf, "%s\n", platform_id);
}
-static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL);
+static DEVICE_ATTR_RO(platform_id);
int skl_nhlt_create_sysfs(struct skl_dev *skl)
{
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 52ba0e3a9e95..65d0bf939134 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -371,7 +371,7 @@ static int jz4740_i2s_resume(struct snd_soc_component *component)
return 0;
}
-static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
+static void jz4740_i2s_init_pcm_config(struct jz4740_i2s *i2s)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -396,7 +396,7 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
if (ret)
return ret;
- jz4740_i2c_init_pcm_config(i2s);
+ jz4740_i2s_init_pcm_config(i2s);
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
&i2s->capture_dma_data);
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
index 44f12016064d..4da14eac1145 100644
--- a/sound/soc/jz4740/jz4740-i2s.h
+++ b/sound/soc/jz4740/jz4740-i2s.h
@@ -7,6 +7,4 @@
#define JZ4740_I2S_CLKSRC_EXT 0
#define JZ4740_I2S_CLKSRC_PLL 1
-#define JZ4740_I2S_BIT_CLK 0
-
#endif
diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c
index f85b5ea180ec..d884bb7c0fc7 100644
--- a/sound/soc/mediatek/common/mtk-btcvsd.c
+++ b/sound/soc/mediatek/common/mtk-btcvsd.c
@@ -1281,7 +1281,7 @@ static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = {
static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
{
- int ret = 0;
+ int ret;
int irq_id;
u32 offset[5] = {0, 0, 0, 0, 0};
struct mtk_btcvsd_snd *btcvsd;
@@ -1337,7 +1337,8 @@ static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
btcvsd->bt_sram_bank2_base = of_iomap(dev->of_node, 1);
if (!btcvsd->bt_sram_bank2_base) {
dev_err(dev, "iomap bt_sram_bank2_base fail\n");
- return -EIO;
+ ret = -EIO;
+ goto unmap_pkv_err;
}
btcvsd->infra = syscon_regmap_lookup_by_phandle(dev->of_node,
@@ -1345,7 +1346,8 @@ static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
if (IS_ERR(btcvsd->infra)) {
dev_err(dev, "cannot find infra controller: %ld\n",
PTR_ERR(btcvsd->infra));
- return PTR_ERR(btcvsd->infra);
+ ret = PTR_ERR(btcvsd->infra);
+ goto unmap_bank2_err;
}
/* get offset */
@@ -1354,7 +1356,7 @@ static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
ARRAY_SIZE(offset));
if (ret) {
dev_warn(dev, "%s(), get offset fail, ret %d\n", __func__, ret);
- return ret;
+ goto unmap_bank2_err;
}
btcvsd->infra_misc_offset = offset[0];
btcvsd->conn_bt_cvsd_mask = offset[1];
@@ -1373,8 +1375,18 @@ static int mtk_btcvsd_snd_probe(struct platform_device *pdev)
mtk_btcvsd_snd_set_state(btcvsd, btcvsd->tx, BT_SCO_STATE_IDLE);
mtk_btcvsd_snd_set_state(btcvsd, btcvsd->rx, BT_SCO_STATE_IDLE);
- return devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform,
- NULL, 0);
+ ret = devm_snd_soc_register_component(dev, &mtk_btcvsd_snd_platform,
+ NULL, 0);
+ if (ret)
+ goto unmap_bank2_err;
+
+ return 0;
+
+unmap_bank2_err:
+ iounmap(btcvsd->bt_sram_bank2_base);
+unmap_pkv_err:
+ iounmap(btcvsd->bt_pkv_base);
+ return ret;
}
static int mtk_btcvsd_snd_remove(struct platform_device *pdev)
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-adda.c b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
index f040dce85da5..f8c73e8624df 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
@@ -413,8 +413,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2)
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x38);
- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2)
- regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x30);
else
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x30);
break;
diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
index 9339fabccb79..1dfee1396843 100644
--- a/sound/soc/meson/g12a-toacodec.c
+++ b/sound/soc/meson/g12a-toacodec.c
@@ -21,17 +21,41 @@
#define TOACODEC_CTRL0 0x0
#define CTRL0_ENABLE_SHIFT 31
-#define CTRL0_DAT_SEL_SHIFT 14
-#define CTRL0_DAT_SEL (0x3 << CTRL0_DAT_SEL_SHIFT)
+#define CTRL0_DAT_SEL_SM1_MSB 19
+#define CTRL0_DAT_SEL_SM1_LSB 18
+#define CTRL0_DAT_SEL_MSB 15
+#define CTRL0_DAT_SEL_LSB 14
+#define CTRL0_LANE_SEL_SM1 16
#define CTRL0_LANE_SEL 12
-#define CTRL0_LRCLK_SEL GENMASK(9, 8)
+#define CTRL0_LRCLK_SEL_SM1_MSB 14
+#define CTRL0_LRCLK_SEL_SM1_LSB 12
+#define CTRL0_LRCLK_SEL_MSB 9
+#define CTRL0_LRCLK_SEL_LSB 8
+#define CTRL0_LRCLK_INV_SM1 BIT(10)
+#define CTRL0_BLK_CAP_INV_SM1 BIT(9)
#define CTRL0_BLK_CAP_INV BIT(7)
+#define CTRL0_BCLK_O_INV_SM1 BIT(8)
#define CTRL0_BCLK_O_INV BIT(6)
-#define CTRL0_BCLK_SEL GENMASK(5, 4)
+#define CTRL0_BCLK_SEL_SM1_MSB 6
+#define CTRL0_BCLK_SEL_MSB 5
+#define CTRL0_BCLK_SEL_LSB 4
#define CTRL0_MCLK_SEL GENMASK(2, 0)
#define TOACODEC_OUT_CHMAX 2
+struct g12a_toacodec {
+ struct regmap_field *field_dat_sel;
+ struct regmap_field *field_lrclk_sel;
+ struct regmap_field *field_bclk_sel;
+};
+
+struct g12a_toacodec_match_data {
+ const struct snd_soc_component_driver *component_drv;
+ struct reg_field field_dat_sel;
+ struct reg_field field_lrclk_sel;
+ struct reg_field field_bclk_sel;
+};
+
static const char * const g12a_toacodec_mux_texts[] = {
"I2S A", "I2S B", "I2S C",
};
@@ -41,29 +65,24 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component =
snd_soc_dapm_kcontrol_component(kcontrol);
+ struct g12a_toacodec *priv = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm =
snd_soc_dapm_kcontrol_dapm(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int mux, changed;
+ unsigned int mux, reg;
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
- changed = snd_soc_component_test_bits(component, e->reg,
- CTRL0_DAT_SEL,
- FIELD_PREP(CTRL0_DAT_SEL, mux));
+ regmap_field_read(priv->field_dat_sel, &reg);
- if (!changed)
+ if (mux == reg)
return 0;
/* Force disconnect of the mux while updating */
snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
- snd_soc_component_update_bits(component, e->reg,
- CTRL0_DAT_SEL |
- CTRL0_LRCLK_SEL |
- CTRL0_BCLK_SEL,
- FIELD_PREP(CTRL0_DAT_SEL, mux) |
- FIELD_PREP(CTRL0_LRCLK_SEL, mux) |
- FIELD_PREP(CTRL0_BCLK_SEL, mux));
+ regmap_field_write(priv->field_dat_sel, mux);
+ regmap_field_write(priv->field_lrclk_sel, mux);
+ regmap_field_write(priv->field_bclk_sel, mux);
/*
* FIXME:
@@ -86,7 +105,11 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
}
static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0,
- CTRL0_DAT_SEL_SHIFT,
+ CTRL0_DAT_SEL_LSB,
+ g12a_toacodec_mux_texts);
+
+static SOC_ENUM_SINGLE_DECL(sm1_toacodec_mux_enum, TOACODEC_CTRL0,
+ CTRL0_DAT_SEL_SM1_LSB,
g12a_toacodec_mux_texts);
static const struct snd_kcontrol_new g12a_toacodec_mux =
@@ -94,6 +117,11 @@ static const struct snd_kcontrol_new g12a_toacodec_mux =
snd_soc_dapm_get_enum_double,
g12a_toacodec_mux_put_enum);
+static const struct snd_kcontrol_new sm1_toacodec_mux =
+ SOC_DAPM_ENUM_EXT("Source", sm1_toacodec_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_toacodec_mux_put_enum);
+
static const struct snd_kcontrol_new g12a_toacodec_out_enable =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0,
CTRL0_ENABLE_SHIFT, 1, 0);
@@ -105,6 +133,13 @@ static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = {
&g12a_toacodec_out_enable),
};
+static const struct snd_soc_dapm_widget sm1_toacodec_widgets[] = {
+ SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
+ &sm1_toacodec_mux),
+ SND_SOC_DAPM_SWITCH("OUT EN", SND_SOC_NOPM, 0, 0,
+ &g12a_toacodec_out_enable),
+};
+
static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -175,6 +210,13 @@ static int g12a_toacodec_component_probe(struct snd_soc_component *c)
CTRL0_BLK_CAP_INV);
}
+static int sm1_toacodec_component_probe(struct snd_soc_component *c)
+{
+ /* Initialize the static clock parameters */
+ return snd_soc_component_write(c, TOACODEC_CTRL0,
+ CTRL0_BLK_CAP_INV_SM1);
+}
+
static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
{ "SRC", "I2S A", "IN A Playback" },
{ "SRC", "I2S B", "IN B Playback" },
@@ -187,6 +229,10 @@ static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
};
+static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
+};
+
static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
.probe = g12a_toacodec_component_probe,
.controls = g12a_toacodec_controls,
@@ -199,25 +245,72 @@ static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
.non_legacy_dai_naming = 1,
};
+static const struct snd_soc_component_driver sm1_toacodec_component_drv = {
+ .probe = sm1_toacodec_component_probe,
+ .controls = sm1_toacodec_controls,
+ .num_controls = ARRAY_SIZE(sm1_toacodec_controls),
+ .dapm_widgets = sm1_toacodec_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sm1_toacodec_widgets),
+ .dapm_routes = g12a_toacodec_routes,
+ .num_dapm_routes = ARRAY_SIZE(g12a_toacodec_routes),
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
static const struct regmap_config g12a_toacodec_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
+static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {
+ .component_drv = &g12a_toacodec_component_drv,
+ .field_dat_sel = REG_FIELD(TOACODEC_CTRL0, 14, 15),
+ .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 8, 9),
+ .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 5),
+};
+
+static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
+ .component_drv = &sm1_toacodec_component_drv,
+ .field_dat_sel = REG_FIELD(TOACODEC_CTRL0, 18, 19),
+ .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, 12, 14),
+ .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, 4, 6),
+};
+
static const struct of_device_id g12a_toacodec_of_match[] = {
- { .compatible = "amlogic,g12a-toacodec", },
+ {
+ .compatible = "amlogic,g12a-toacodec",
+ .data = &g12a_toacodec_match_data,
+ },
+ {
+ .compatible = "amlogic,sm1-toacodec",
+ .data = &sm1_toacodec_match_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match);
static int g12a_toacodec_probe(struct platform_device *pdev)
{
+ const struct g12a_toacodec_match_data *data;
struct device *dev = &pdev->dev;
+ struct g12a_toacodec *priv;
void __iomem *regs;
struct regmap *map;
int ret;
+ data = device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
ret = device_reset(dev);
if (ret)
return ret;
@@ -233,8 +326,20 @@ static int g12a_toacodec_probe(struct platform_device *pdev)
return PTR_ERR(map);
}
+ priv->field_dat_sel = devm_regmap_field_alloc(dev, map, data->field_dat_sel);
+ if (IS_ERR(priv->field_dat_sel))
+ return PTR_ERR(priv->field_dat_sel);
+
+ priv->field_lrclk_sel = devm_regmap_field_alloc(dev, map, data->field_lrclk_sel);
+ if (IS_ERR(priv->field_lrclk_sel))
+ return PTR_ERR(priv->field_lrclk_sel);
+
+ priv->field_bclk_sel = devm_regmap_field_alloc(dev, map, data->field_bclk_sel);
+ if (IS_ERR(priv->field_bclk_sel))
+ return PTR_ERR(priv->field_bclk_sel);
+
return devm_snd_soc_register_component(dev,
- &g12a_toacodec_component_drv, g12a_toacodec_dai_drv,
+ data->component_drv, g12a_toacodec_dai_drv,
ARRAY_SIZE(g12a_toacodec_dai_drv));
}
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index c62d2612e8f5..28c7497344e3 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -835,18 +835,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
if (dai_id == LPASS_DP_RX)
continue;
- drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev,
+ drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
variant->dai_osr_clk_names[i]);
- if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
- dev_warn(dev,
- "%s() error getting optional %s: %ld\n",
- __func__,
- variant->dai_osr_clk_names[i],
- PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
-
- drvdata->mi2s_osr_clk[dai_id] = NULL;
- }
-
drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev,
variant->dai_bit_clk_names[i]);
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 9766725c2916..5ff56a735419 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -1169,7 +1169,7 @@ static int q6asm_dai_compr_get_codec_caps(struct snd_soc_component *component,
return 0;
}
-static struct snd_compress_ops q6asm_dai_compress_ops = {
+static const struct snd_compress_ops q6asm_dai_compress_ops = {
.open = q6asm_dai_compr_open,
.free = q6asm_dai_compr_free,
.set_params = q6asm_dai_compr_set_params,
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 153e9b2de0b5..0adfc5708949 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -288,6 +288,14 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dai_set_sysclk(codec_dai, 0,
WCD934X_DEFAULT_MCLK_RATE,
SNDRV_PCM_STREAM_PLAYBACK);
+
+ rval = snd_soc_component_set_jack(codec_dai->component,
+ &pdata->jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ return rval;
+ }
+
}
break;
default:
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 3c574792231b..3c934f87c242 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -1694,12 +1694,27 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/*
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static u64 fsi_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops fsi_dai_ops = {
.startup = fsi_dai_startup,
.shutdown = fsi_dai_shutdown,
.trigger = fsi_dai_trigger,
.set_fmt = fsi_dai_set_fmt,
.hw_params = fsi_dai_hw_params,
+ .auto_selectable_formats = &fsi_dai_formats,
+ .num_auto_selectable_formats = 1,
};
/*
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index 5d1ff8ef26f9..d07eccfa3ac2 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
-snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
+snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 0b8ae3eee148..0ebee1ed06a9 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -3,8 +3,8 @@
// Helper routines for R-Car sound ADG.
//
// Copyright (C) 2013 Kuninori Morimoto <[email protected]>
-
#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include "rsnd.h"
#define CLKA 0
@@ -28,6 +28,7 @@ static struct rsnd_mod_ops adg_ops = {
struct rsnd_adg {
struct clk *clk[CLKMAX];
struct clk *clkout[CLKOUTMAX];
+ struct clk *null_clk;
struct clk_onecell_data onecell;
struct rsnd_mod mod;
int clk_rate[CLKMAX];
@@ -290,7 +291,6 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct clk *clk;
int i;
int sel_table[] = {
[CLKA] = 0x1,
@@ -303,10 +303,9 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
* find suitable clock from
* AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
*/
- for_each_rsnd_clk(clk, adg, i) {
+ for (i = 0; i < CLKMAX; i++)
if (rate == adg->clk_rate[i])
return sel_table[i];
- }
/*
* find divided clock from BRGA/BRGB
@@ -365,48 +364,103 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk;
int i;
for_each_rsnd_clk(clk, adg, i) {
if (enable) {
- int ret = clk_prepare_enable(clk);
+ clk_prepare_enable(clk);
/*
* We shouldn't use clk_get_rate() under
* atomic context. Let's keep it when
* rsnd_adg_clk_enable() was called
*/
- adg->clk_rate[i] = 0;
- if (ret < 0)
- dev_warn(dev, "can't use clk %d\n", i);
- else
- adg->clk_rate[i] = clk_get_rate(clk);
+ adg->clk_rate[i] = clk_get_rate(clk);
} else {
- if (adg->clk_rate[i])
- clk_disable_unprepare(clk);
- adg->clk_rate[i] = 0;
+ clk_disable_unprepare(clk);
}
}
}
-static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
- struct rsnd_adg *adg)
+static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv,
+ const char * const name,
+ const char *parent)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(dev, name, parent, 0, 0);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "create null clk error\n");
+ return NULL;
+ }
+
+ return clk;
+}
+
+static struct clk *rsnd_adg_null_clk_get(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
+
+ if (!adg->null_clk) {
+ static const char * const name = "rsnd_adg_null";
+
+ adg->null_clk = rsnd_adg_create_null_clk(priv, name, NULL);
+ }
+
+ return adg->null_clk;
+}
+
+static void rsnd_adg_null_clk_clean(struct rsnd_priv *priv)
{
+ struct rsnd_adg *adg = priv->adg;
+
+ if (adg->null_clk)
+ clk_unregister_fixed_rate(adg->null_clk);
+}
+
+static int rsnd_adg_get_clkin(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
struct device *dev = rsnd_priv_to_dev(priv);
+ struct clk *clk;
int i;
for (i = 0; i < CLKMAX; i++) {
- struct clk *clk = devm_clk_get(dev, clk_name[i]);
+ clk = devm_clk_get(dev, clk_name[i]);
+
+ if (IS_ERR(clk))
+ clk = rsnd_adg_null_clk_get(priv);
+ if (IS_ERR(clk))
+ goto err;
- adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+ adg->clk[i] = clk;
}
+
+ return 0;
+
+err:
+ dev_err(dev, "adg clock IN get failed\n");
+
+ rsnd_adg_null_clk_clean(priv);
+
+ return -EIO;
}
-static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
- struct rsnd_adg *adg)
+static void rsnd_adg_unregister_clkout(struct rsnd_priv *priv)
{
+ struct rsnd_adg *adg = priv->adg;
+ struct clk *clk;
+ int i;
+
+ for_each_rsnd_clkout(clk, adg, i)
+ clk_unregister_fixed_rate(clk);
+}
+
+static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
+{
+ struct rsnd_adg *adg = priv->adg;
struct clk *clk;
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
@@ -446,9 +500,8 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
req_size = prop->length / sizeof(u32);
if (req_size > REQ_SIZE) {
- dev_err(dev,
- "too many clock-frequency, use top %d\n", REQ_SIZE);
- req_size = REQ_SIZE;
+ dev_err(dev, "too many clock-frequency\n");
+ return -EINVAL;
}
of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
@@ -529,10 +582,11 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
if (!count) {
clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
parent_clk_name, 0, req_rate[0]);
- if (!IS_ERR(clk)) {
- adg->clkout[CLKOUT] = clk;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
- }
+ if (IS_ERR(clk))
+ goto err;
+
+ adg->clkout[CLKOUT] = clk;
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
/*
* for clkout0/1/2/3
@@ -542,8 +596,10 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
clk = clk_register_fixed_rate(dev, clkout_name[i],
parent_clk_name, 0,
req_rate[0]);
- if (!IS_ERR(clk))
- adg->clkout[i] = clk;
+ if (IS_ERR(clk))
+ goto err;
+
+ adg->clkout[i] = clk;
}
adg->onecell.clks = adg->clkout;
adg->onecell.clk_num = CLKOUTMAX;
@@ -555,34 +611,61 @@ rsnd_adg_get_clkout_end:
adg->ckr = ckr;
adg->rbga = rbga;
adg->rbgb = rbgb;
+
+ return 0;
+
+err:
+ dev_err(dev, "adg clock OUT get failed\n");
+
+ rsnd_adg_unregister_clkout(priv);
+
+ return -EIO;
}
-#ifdef DEBUG
-static void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct rsnd_adg *adg)
+#if defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+__printf(3, 4)
+static void dbg_msg(struct device *dev, struct seq_file *m,
+ const char *fmt, ...)
{
+ char msg[128];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ va_end(args);
+
+ if (m)
+ seq_puts(m, msg);
+ else
+ dev_dbg(dev, "%s", msg);
+}
+
+void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk;
int i;
for_each_rsnd_clk(clk, adg, i)
- dev_dbg(dev, "%s : %pa : %ld\n",
+ dbg_msg(dev, m, "%s : %pa : %ld\n",
clk_name[i], clk, clk_get_rate(clk));
- dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+ dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
adg->ckr, adg->rbga, adg->rbgb);
- dev_dbg(dev, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz);
- dev_dbg(dev, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz);
+ dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz);
+ dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz);
/*
* Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start()
* by BRGCKR::BRGCKR_31
*/
for_each_rsnd_clkout(clk, adg, i)
- dev_dbg(dev, "clkout %d : %pa : %ld\n", i,
+ dbg_msg(dev, m, "clkout %d : %pa : %ld\n", i,
clk, clk_get_rate(clk));
}
#else
-#define rsnd_adg_clk_dbg_info(priv, adg)
+#define rsnd_adg_clk_dbg_info(priv, m)
#endif
int rsnd_adg_probe(struct rsnd_priv *priv)
@@ -600,13 +683,18 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
if (ret)
return ret;
- rsnd_adg_get_clkin(priv, adg);
- rsnd_adg_get_clkout(priv, adg);
- rsnd_adg_clk_dbg_info(priv, adg);
-
priv->adg = adg;
+ ret = rsnd_adg_get_clkin(priv);
+ if (ret)
+ return ret;
+
+ ret = rsnd_adg_get_clkout(priv);
+ if (ret)
+ return ret;
+
rsnd_adg_clk_enable(priv);
+ rsnd_adg_clk_dbg_info(priv, NULL);
return 0;
}
@@ -615,15 +703,13 @@ void rsnd_adg_remove(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
- struct rsnd_adg *adg = priv->adg;
- struct clk *clk;
- int i;
- for_each_rsnd_clkout(clk, adg, i)
- if (adg->clkout[i])
- clk_unregister_fixed_rate(adg->clkout[i]);
+ rsnd_adg_unregister_clkout(priv);
of_clk_del_provider(np);
rsnd_adg_clk_disable(priv);
+
+ /* It should be called after rsnd_adg_clk_disable() */
+ rsnd_adg_null_clk_clean(priv);
}
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
index 9fdb37c2cbc2..329e6ab1b222 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/sh/rcar/cmd.c
@@ -114,12 +114,26 @@ static int rsnd_cmd_stop(struct rsnd_mod *mod,
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_cmd_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0x180 + rsnd_mod_id_raw(mod) * 0x20, 0x30);
+}
+#define DEBUG_INFO .debug_info = rsnd_cmd_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_cmd_ops = {
.name = CMD_NAME,
.init = rsnd_cmd_init,
.start = rsnd_cmd_start,
.stop = rsnd_cmd_stop,
.get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 8696a993c478..5e382b5c9d45 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -90,14 +90,6 @@
*
*/
-/*
- * you can enable below define if you don't need
- * DAI status debug message when debugging
- * see rsnd_dbg_dai_call()
- *
- * #define RSND_DEBUG_NO_DAI_CALL 1
- */
-
#include <linux/pm_runtime.h>
#include "rsnd.h"
@@ -267,8 +259,9 @@ int rsnd_runtime_channel_original_with_params(struct rsnd_dai_stream *io,
*/
if (params)
return params_channels(params);
- else
+ else if (runtime)
return runtime->channels;
+ return 0;
}
int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io,
@@ -533,16 +526,22 @@ static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
},
};
-static int rsnd_status_update(u32 *status,
+static int rsnd_status_update(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod, enum rsnd_mod_type type,
int shift, int add, int timing)
{
+ u32 *status = mod->ops->get_status(mod, io, type);
u32 mask = 0xF << shift;
u8 val = (*status >> shift) & 0xF;
u8 next_val = (val + add) & 0xF;
int func_call = (val == timing);
+ /* no status update */
+ if (add == 0 || shift == 28)
+ return 1;
+
if (next_val == 0xF) /* underflow case */
- func_call = 0;
+ func_call = -1;
else
*status = (*status & ~mask) + (next_val << shift);
@@ -558,19 +557,16 @@ static int rsnd_status_update(u32 *status,
enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \
for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \
int tmp = 0; \
- u32 *status = mod->ops->get_status(mod, io, types[i]); \
- int func_call = rsnd_status_update(status, \
+ int func_call = rsnd_status_update(io, mod, types[i], \
__rsnd_mod_shift_##fn, \
__rsnd_mod_add_##fn, \
__rsnd_mod_call_##fn); \
- rsnd_dbg_dai_call(dev, "%s\t0x%08x %s\n", \
- rsnd_mod_name(mod), *status, \
- (func_call && (mod)->ops->fn) ? #fn : ""); \
- if (func_call && (mod)->ops->fn) \
+ if (func_call > 0 && (mod)->ops->fn) \
tmp = (mod)->ops->fn(mod, io, param); \
- if (tmp && (tmp != -EPROBE_DEFER)) \
- dev_err(dev, "%s : %s error %d\n", \
- rsnd_mod_name(mod), #fn, tmp); \
+ if (unlikely(func_call < 0) || \
+ unlikely(tmp && (tmp != -EPROBE_DEFER))) \
+ dev_err(dev, "%s : %s error (%d, %d)\n", \
+ rsnd_mod_name(mod), #fn, tmp, func_call);\
ret |= tmp; \
} \
ret; \
@@ -760,10 +756,10 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
/* set clock master for audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rdai->clk_master = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
rdai->clk_master = 1; /* cpu is master */
break;
default:
@@ -1043,6 +1039,31 @@ static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream,
return rsnd_dai_call(prepare, io, priv);
}
+static u64 rsnd_soc_dai_formats[] = {
+ /*
+ * 1st Priority
+ *
+ * Well tested formats.
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /*
+ * 2nd Priority
+ *
+ * Supported, but not well tested
+ */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
.startup = rsnd_soc_dai_startup,
.shutdown = rsnd_soc_dai_shutdown,
@@ -1050,6 +1071,8 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
.set_fmt = rsnd_soc_dai_set_fmt,
.set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
.prepare = rsnd_soc_dai_prepare,
+ .auto_selectable_formats = rsnd_soc_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats),
};
static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
@@ -1129,7 +1152,7 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv,
of_node_put(remote_node);
}
-void rsnd_parse_connect_common(struct rsnd_dai *rdai,
+void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
struct device_node *playback,
@@ -1144,7 +1167,11 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
i = 0;
for_each_child_of_node(node, np) {
- struct rsnd_mod *mod = mod_get(priv, i);
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(np, name, i);
+
+ mod = mod_get(priv, i);
if (np == playback)
rsnd_dai_connect(mod, &rdai->playback, mod->type);
@@ -1156,6 +1183,56 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
of_node_put(node);
}
+int rsnd_node_fixed_index(struct device_node *node, char *name, int idx)
+{
+ char node_name[16];
+
+ /*
+ * rsnd is assuming each device nodes are sequential numbering,
+ * but some of them are not.
+ * This function adjusts index for it.
+ *
+ * ex)
+ * Normal case, special case
+ * ssi-0
+ * ssi-1
+ * ssi-2
+ * ssi-3 ssi-3
+ * ssi-4 ssi-4
+ * ...
+ *
+ * assume Max 64 node
+ */
+ for (; idx < 64; idx++) {
+ snprintf(node_name, sizeof(node_name), "%s-%d", name, idx);
+
+ if (strncmp(node_name, of_node_full_name(node), sizeof(node_name)) == 0)
+ return idx;
+ }
+
+ return -EINVAL;
+}
+
+int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np;
+ int i;
+
+ i = 0;
+ for_each_child_of_node(node, np) {
+ i = rsnd_node_fixed_index(np, name, i);
+ if (i < 0) {
+ dev_err(dev, "strange node numbering (%s)",
+ of_node_full_name(node));
+ return 0;
+ }
+ i++;
+ }
+
+ return i;
+}
+
static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
int *is_graph)
{
@@ -1388,6 +1465,26 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
/*
* pcm ops
*/
+static int rsnd_hw_update(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (hw_params)
+ ret = rsnd_dai_call(hw_params, io, substream, hw_params);
+ else
+ ret = rsnd_dai_call(hw_free, io, substream);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
static int rsnd_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
@@ -1495,17 +1592,13 @@ static int rsnd_hw_params(struct snd_soc_component *component,
}
}
- return rsnd_dai_call(hw_params, io, substream, hw_params);
+ return rsnd_hw_update(substream, hw_params);
}
static int rsnd_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
-
- return rsnd_dai_call(hw_free, io, substream);
+ return rsnd_hw_update(substream, NULL);
}
static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component,
@@ -1715,6 +1808,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
*/
static const struct snd_soc_component_driver rsnd_soc_component = {
.name = "rsnd",
+ .probe = rsnd_debugfs_probe,
.hw_params = rsnd_hw_params,
.hw_free = rsnd_hw_free,
.pointer = rsnd_pointer,
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
index 20eecd088d13..6156445bcb69 100644
--- a/sound/soc/sh/rcar/ctu.c
+++ b/sound/soc/sh/rcar/ctu.c
@@ -275,6 +275,19 @@ static int rsnd_ctu_id_sub(struct rsnd_mod *mod)
return mod->id % 4;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ctu_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0x500 + rsnd_mod_id_raw(mod) * 0x100, 0x100);
+}
+#define DEBUG_INFO .debug_info = rsnd_ctu_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_ctu_ops = {
.name = CTU_NAME,
.probe = rsnd_ctu_probe_,
@@ -285,6 +298,7 @@ static struct rsnd_mod_ops rsnd_ctu_ops = {
.id = rsnd_ctu_id,
.id_sub = rsnd_ctu_id_sub,
.id_cmd = rsnd_mod_id_raw,
+ DEBUG_INFO
};
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
diff --git a/sound/soc/sh/rcar/debugfs.c b/sound/soc/sh/rcar/debugfs.c
new file mode 100644
index 000000000000..26d3b310b9db
--- /dev/null
+++ b/sound/soc/sh/rcar/debugfs.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// // Renesas R-Car debugfs support
+//
+// Copyright (c) 2021 Kuninori Morimoto <[email protected]>
+//
+// > mount -t debugfs none /sys/kernel/debug
+// > cd /sys/kernel/debug/asoc/rcar-sound/ec500000.sound/rdai{N}/
+// > cat playback/xxx
+// > cat capture/xxx
+//
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include "rsnd.h"
+
+static int rsnd_debugfs_show(struct seq_file *m, void *v)
+{
+ struct rsnd_dai_stream *io = m->private;
+ struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int i;
+
+ /* adg is out of mods */
+ rsnd_adg_clk_dbg_info(priv, m);
+
+ for_each_rsnd_mod(i, mod, io) {
+ u32 *status = mod->ops->get_status(mod, io, mod->type);
+
+ seq_printf(m, "name: %s\n", rsnd_mod_name(mod));
+ seq_printf(m, "status: %08x\n", *status);
+
+ if (mod->ops->debug_info)
+ mod->ops->debug_info(m, io, mod);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rsnd_debugfs);
+
+void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr,
+ void __iomem *base, int offset, int size)
+{
+ int i, j;
+
+ for (i = 0; i < size; i += 0x10) {
+ phys_addr_t addr = _addr + offset + i;
+
+ seq_printf(m, "%pa:", &addr);
+ for (j = 0; j < 0x10; j += 0x4)
+ seq_printf(m, " %08x", __raw_readl(base + offset + i + j));
+ seq_puts(m, "\n");
+ }
+}
+
+void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod,
+ int reg_id, int offset, int size)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+ rsnd_debugfs_reg_show(m,
+ rsnd_gen_get_phy_addr(priv, reg_id),
+ rsnd_gen_get_base_addr(priv, reg_id),
+ offset, size);
+}
+
+int rsnd_debugfs_probe(struct snd_soc_component *component)
+{
+ struct rsnd_priv *priv = dev_get_drvdata(component->dev);
+ struct rsnd_dai *rdai;
+ struct dentry *dir;
+ char name[64];
+ int i;
+
+ /* Gen1 is not supported */
+ if (rsnd_is_gen1(priv))
+ return 0;
+
+ for_each_rsnd_dai(rdai, priv, i) {
+ /*
+ * created debugfs will be automatically
+ * removed, nothing to do for _remove.
+ * see
+ * soc_cleanup_component_debugfs()
+ */
+ snprintf(name, sizeof(name), "rdai%d", i);
+ dir = debugfs_create_dir(name, component->debugfs_root);
+
+ debugfs_create_file("playback", 0444, dir, &rdai->playback, &rsnd_debugfs_fops);
+ debugfs_create_file("capture", 0444, dir, &rdai->capture, &rsnd_debugfs_fops);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 95aa26d62e4f..82d16e037d9a 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -44,7 +44,8 @@ struct rsnd_dma {
};
struct rsnd_dma_ctrl {
- void __iomem *base;
+ void __iomem *ppbase;
+ phys_addr_t ppres;
int dmaen_num;
int dmapp_num;
};
@@ -236,16 +237,18 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
return 0;
}
-struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
- struct rsnd_mod *mod, char *name)
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
+ struct rsnd_mod *mod, char *x)
{
struct dma_chan *chan = NULL;
struct device_node *np;
int i = 0;
for_each_child_of_node(of_node, np) {
+ i = rsnd_node_fixed_index(np, name, i);
+
if (i == rsnd_mod_id_raw(mod) && (!chan))
- chan = of_dma_request_slave_channel(np, name);
+ chan = of_dma_request_slave_channel(np, x);
i++;
}
@@ -415,7 +418,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
}
#define rsnd_dmapp_addr(dmac, dma, reg) \
- (dmac->base + 0x20 + reg + \
+ (dmac->ppbase + 0x20 + reg + \
(0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
{
@@ -504,12 +507,31 @@ static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_dmapp_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+ struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+
+ rsnd_debugfs_reg_show(m, dmac->ppres, dmac->ppbase,
+ 0x20 + 0x10 * dmapp->dmapp_id, 0x10);
+}
+#define DEBUG_INFO .debug_info = rsnd_dmapp_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_dmapp_ops = {
.name = "audmac-pp",
.start = rsnd_dmapp_start,
.stop = rsnd_dmapp_stop,
.quit = rsnd_dmapp_stop,
.get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
/*
@@ -864,9 +886,10 @@ int rsnd_dma_probe(struct rsnd_priv *priv)
}
dmac->dmapp_num = 0;
- dmac->base = devm_ioremap_resource(dev, res);
- if (IS_ERR(dmac->base))
- return PTR_ERR(dmac->base);
+ dmac->ppres = res->start;
+ dmac->ppbase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dmac->ppbase))
+ return PTR_ERR(dmac->ppbase);
priv->dma = dmac;
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 8d91c0eb0880..5137e03a9d7c 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -282,9 +282,22 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
- mod, "tx");
+ DVC_NAME, mod, "tx");
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_dvc_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0xe00 + rsnd_mod_id(mod) * 0x100, 0x60);
+}
+#define DEBUG_INFO .debug_info = rsnd_dvc_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = DVC_NAME,
.dma_req = rsnd_dvc_dma_req,
@@ -293,6 +306,7 @@ static struct rsnd_mod_ops rsnd_dvc_ops = {
.quit = rsnd_dvc_quit,
.pcm_new = rsnd_dvc_pcm_new,
.get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 8bd49c8a9517..925565baaa41 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -141,6 +141,15 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
return gen->res[reg_id];
}
+#ifdef CONFIG_DEBUG_FS
+void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id)
+{
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+ return gen->base[reg_id];
+}
+#endif
+
#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \
_rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
index a3e0370f5704..3572c2c5686c 100644
--- a/sound/soc/sh/rcar/mix.c
+++ b/sound/soc/sh/rcar/mix.c
@@ -250,6 +250,19 @@ static int rsnd_mix_pcm_new(struct rsnd_mod *mod,
return ret;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_mix_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0xd00 + rsnd_mod_id(mod) * 0x40, 0x30);
+}
+#define DEBUG_INFO .debug_info = rsnd_mix_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_mix_ops = {
.name = MIX_NAME,
.probe = rsnd_mix_probe_,
@@ -257,6 +270,7 @@ static struct rsnd_mod_ops rsnd_mix_ops = {
.quit = rsnd_mix_quit,
.pcm_new = rsnd_mix_pcm_new,
.get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 1255a85151db..6580bab0e229 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -269,8 +269,8 @@ u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
int rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, struct rsnd_mod **dma_mod);
int rsnd_dma_probe(struct rsnd_priv *priv);
-struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
- struct rsnd_mod *mod, char *name);
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name,
+ struct rsnd_mod *mod, char *x);
/*
* R-Car sound mod
@@ -345,6 +345,11 @@ struct rsnd_mod_ops {
int (*id)(struct rsnd_mod *mod);
int (*id_sub)(struct rsnd_mod *mod);
int (*id_cmd)(struct rsnd_mod *mod);
+
+#ifdef CONFIG_DEBUG_FS
+ void (*debug_info)(struct seq_file *m,
+ struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+#endif
};
struct rsnd_dai_stream;
@@ -359,19 +364,13 @@ struct rsnd_mod {
/*
* status
*
- * 0xH0000CB0
+ * 0xH000DCB0
*
* B 0: init 1: quit
* C 0: start 1: stop
* D 0: hw_params 1: hw_free
*
* H is always called (see __rsnd_mod_call)
- * H 0: probe 1: remove
- * H 0: pcm_new
- * H 0: fallback
- * H 0: pointer
- * H 0: prepare
- * H 0: cleanup
*/
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
@@ -392,12 +391,12 @@ struct rsnd_mod {
#define __rsnd_mod_add_remove 0
#define __rsnd_mod_add_prepare 0
#define __rsnd_mod_add_cleanup 0
-#define __rsnd_mod_add_init 1
-#define __rsnd_mod_add_quit -1
-#define __rsnd_mod_add_start 1
-#define __rsnd_mod_add_stop -1
-#define __rsnd_mod_add_hw_params 1
-#define __rsnd_mod_add_hw_free -1
+#define __rsnd_mod_add_init 1 /* needs protect */
+#define __rsnd_mod_add_quit -1 /* needs protect */
+#define __rsnd_mod_add_start 1 /* needs protect */
+#define __rsnd_mod_add_stop -1 /* needs protect */
+#define __rsnd_mod_add_hw_params 1 /* needs protect */
+#define __rsnd_mod_add_hw_free -1 /* needs protect */
#define __rsnd_mod_add_irq 0
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
@@ -407,16 +406,16 @@ struct rsnd_mod {
#define __rsnd_mod_call_remove 0
#define __rsnd_mod_call_prepare 0
#define __rsnd_mod_call_cleanup 0
-#define __rsnd_mod_call_init 0
-#define __rsnd_mod_call_quit 1
-#define __rsnd_mod_call_start 0
-#define __rsnd_mod_call_stop 1
+#define __rsnd_mod_call_init 0 /* needs protect */
+#define __rsnd_mod_call_quit 1 /* needs protect */
+#define __rsnd_mod_call_start 0 /* needs protect */
+#define __rsnd_mod_call_stop 1 /* needs protect */
+#define __rsnd_mod_call_hw_params 0 /* needs protect */
+#define __rsnd_mod_call_hw_free 1 /* needs protect */
#define __rsnd_mod_call_irq 0
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
-#define __rsnd_mod_call_hw_params 0
#define __rsnd_mod_call_pointer 0
-#define __rsnd_mod_call_hw_free 1
#define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
@@ -455,11 +454,13 @@ struct rsnd_mod *rsnd_mod_next(int *iterator,
#define for_each_rsnd_mod_array(iterator, pos, io, array) \
for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
-void rsnd_parse_connect_common(struct rsnd_dai *rdai,
+void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
struct device_node *playback,
struct device_node *capture);
+int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name);
+int rsnd_node_fixed_index(struct device_node *node, char *name, int idx);
int rsnd_channel_normalization(int chan);
#define rsnd_runtime_channel_original(io) \
@@ -592,6 +593,9 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg);
phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
+#ifdef CONFIG_DEBUG_FS
+void __iomem *rsnd_gen_get_base_addr(struct rsnd_priv *priv, int reg_id);
+#endif
/*
* R-Car ADG
@@ -610,6 +614,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
#define rsnd_adg_clk_enable(priv) rsnd_adg_clk_control(priv, 1)
#define rsnd_adg_clk_disable(priv) rsnd_adg_clk_control(priv, 0)
void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
+void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m);
/*
* R-Car sound priv
@@ -776,6 +781,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_secondaries_runtime(struct rsnd_dai_stream *io);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -799,6 +805,7 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
struct device_node *playback,
struct device_node *capture);
#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU)
+bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod);
/*
* R-Car SRC
@@ -815,7 +822,7 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
#define rsnd_src_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SRC)
#define rsnd_parse_connect_src(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
+ rsnd_parse_connect_common(rdai, "src", rsnd_src_mod_get, \
rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
@@ -827,7 +834,7 @@ void rsnd_ctu_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU)
#define rsnd_parse_connect_ctu(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
+ rsnd_parse_connect_common(rdai, "ctu", rsnd_ctu_mod_get, \
rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
@@ -839,7 +846,7 @@ void rsnd_mix_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_mix_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_MIX)
#define rsnd_parse_connect_mix(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
+ rsnd_parse_connect_common(rdai, "mix", rsnd_mix_mod_get, \
rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
@@ -851,7 +858,7 @@ void rsnd_dvc_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_dvc_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DVC)
#define rsnd_parse_connect_dvc(rdai, playback, capture) \
- rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
+ rsnd_parse_connect_common(rdai, "dvc", rsnd_dvc_mod_get, \
rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
@@ -879,9 +886,10 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
*
* #define RSND_DEBUG_NO_IRQ_STATUS 1
*/
-#define rsnd_dbg_irq_status(dev, param...) \
+#define rsnd_print_irq_status(dev, param...) do { \
if (!IS_BUILTIN(RSND_DEBUG_NO_IRQ_STATUS)) \
- dev_dbg(dev, param)
+ dev_info(dev, param); \
+} while (0)
/*
* If you don't need rsnd_dai_call debug message,
@@ -894,3 +902,14 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
dev_dbg(dev, param)
#endif
+
+#ifdef CONFIG_DEBUG_FS
+int rsnd_debugfs_probe(struct snd_soc_component *component);
+void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr,
+ void __iomem *base, int offset, int size);
+void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod,
+ int reg_id, int offset, int size);
+
+#else
+#define rsnd_debugfs_probe NULL
+#endif
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 628af8f3920d..42a100c6303d 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -17,7 +17,7 @@
/*
* you can enable below define if you don't need
* SSI interrupt status debug message when debugging
- * see rsnd_dbg_irq_status()
+ * see rsnd_print_irq_status()
*
* #define RSND_DEBUG_NO_IRQ_STATUS 1
*/
@@ -82,7 +82,7 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
int is_play = rsnd_io_is_play(io);
return rsnd_dma_request_channel(rsnd_src_of_node(priv),
- mod,
+ SRC_NAME, mod,
is_play ? "rx" : "tx");
}
@@ -421,8 +421,8 @@ static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0);
status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1);
if ((status0 & val0) || (status1 & val1)) {
- rsnd_dbg_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n",
- rsnd_mod_name(mod), status0, status1);
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n",
+ rsnd_mod_name(mod), status0, status1);
ret = true;
}
@@ -597,6 +597,25 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
return ret;
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_src_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ rsnd_mod_id(mod) * 0x20, 0x20);
+ seq_puts(m, "\n");
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0x1c0, 0x20);
+ seq_puts(m, "\n");
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SCU,
+ 0x200 + rsnd_mod_id(mod) * 0x40, 0x40);
+}
+#define DEBUG_INFO .debug_info = rsnd_src_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_src_ops = {
.name = SRC_NAME,
.dma_req = rsnd_src_dma_req,
@@ -608,6 +627,7 @@ static struct rsnd_mod_ops rsnd_src_ops = {
.irq = rsnd_src_irq,
.pcm_new = rsnd_src_pcm_new,
.get_status = rsnd_mod_get_status,
+ DEBUG_INFO
};
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -636,7 +656,7 @@ int rsnd_src_probe(struct rsnd_priv *priv)
if (!node)
return 0; /* not used is not error */
- nr = of_get_child_count(node);
+ nr = rsnd_node_count(priv, node, SRC_NAME);
if (!nr) {
ret = -EINVAL;
goto rsnd_src_probe_done;
@@ -656,6 +676,8 @@ int rsnd_src_probe(struct rsnd_priv *priv)
if (!of_device_is_available(np))
goto skip;
+ i = rsnd_node_fixed_index(np, SRC_NAME, i);
+
src = rsnd_src_get(priv, i);
snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index e29482c26d6a..27f34ca6059d 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -11,7 +11,7 @@
/*
* you can enable below define if you don't need
* SSI interrupt status debug message when debugging
- * see rsnd_dbg_irq_status()
+ * see rsnd_print_irq_status()
*
* #define RSND_DEBUG_NO_IRQ_STATUS 1
*/
@@ -117,8 +117,6 @@ struct rsnd_ssi {
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod))
-static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
-
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
@@ -359,96 +357,6 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
rsnd_adg_ssi_clk_stop(mod);
}
-/* enable busif buffer over/under run interrupt. */
-#define rsnd_ssi_busif_err_irq_enable(mod) rsnd_ssi_busif_err_irq_ctrl(mod, 1)
-#define rsnd_ssi_busif_err_irq_disable(mod) rsnd_ssi_busif_err_irq_ctrl(mod, 0)
-static void rsnd_ssi_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable)
-{
- u32 sys_int_enable = 0;
- int id = rsnd_mod_id(mod);
- int i;
-
- switch (id) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- for (i = 0; i < 4; i++) {
- sys_int_enable = rsnd_mod_read(mod, SSI_SYS_INT_ENABLE(i * 2));
- if (enable)
- sys_int_enable |= 0xf << (id * 4);
- else
- sys_int_enable &= ~(0xf << (id * 4));
- rsnd_mod_write(mod,
- SSI_SYS_INT_ENABLE(i * 2),
- sys_int_enable);
- }
- break;
- case 9:
- for (i = 0; i < 4; i++) {
- sys_int_enable = rsnd_mod_read(mod, SSI_SYS_INT_ENABLE((i * 2) + 1));
- if (enable)
- sys_int_enable |= 0xf << 4;
- else
- sys_int_enable &= ~(0xf << 4);
- rsnd_mod_write(mod,
- SSI_SYS_INT_ENABLE((i * 2) + 1),
- sys_int_enable);
- }
- break;
- }
-}
-
-static bool rsnd_ssi_busif_err_status_clear(struct rsnd_mod *mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 status;
- bool stop = false;
- int id = rsnd_mod_id(mod);
- int i;
-
- switch (id) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- for (i = 0; i < 4; i++) {
- status = rsnd_mod_read(mod, SSI_SYS_STATUS(i * 2));
- status &= 0xf << (id * 4);
-
- if (status) {
- rsnd_dbg_irq_status(dev, "%s err status : 0x%08x\n",
- rsnd_mod_name(mod), status);
- rsnd_mod_write(mod,
- SSI_SYS_STATUS(i * 2),
- 0xf << (id * 4));
- stop = true;
- }
- }
- break;
- case 9:
- for (i = 0; i < 4; i++) {
- status = rsnd_mod_read(mod, SSI_SYS_STATUS((i * 2) + 1));
- status &= 0xf << 4;
-
- if (status) {
- rsnd_dbg_irq_status(dev, "%s err status : 0x%08x\n",
- rsnd_mod_name(mod), status);
- rsnd_mod_write(mod,
- SSI_SYS_STATUS((i * 2) + 1),
- 0xf << 4);
- stop = true;
- }
- }
- break;
- }
-
- return stop;
-}
-
static void rsnd_ssi_config_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
@@ -536,10 +444,6 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
cr_mode = DIEN; /* PIO : enable Data interrupt */
}
- /* enable busif buffer over/under run interrupt. */
- if (is_tdm || is_tdm_split)
- rsnd_ssi_busif_err_irq_enable(mod);
-
init_end:
ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
@@ -594,10 +498,6 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
- int is_tdm, is_tdm_split;
-
- is_tdm = rsnd_runtime_is_tdm(io);
- is_tdm_split = rsnd_runtime_is_tdm_split(io);
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
@@ -619,10 +519,6 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
ssi->wsr = 0;
}
- /* disable busif buffer over/under run interrupt. */
- if (is_tdm || is_tdm_split)
- rsnd_ssi_busif_err_irq_disable(mod);
-
return 0;
}
@@ -775,10 +671,6 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
u32 status;
bool elapsed = false;
bool stop = false;
- int is_tdm, is_tdm_split;
-
- is_tdm = rsnd_runtime_is_tdm(io);
- is_tdm_split = rsnd_runtime_is_tdm_split(io);
spin_lock(&priv->lock);
@@ -794,14 +686,13 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
/* DMA only */
if (is_dma && (status & (UIRQ | OIRQ))) {
- rsnd_dbg_irq_status(dev, "%s err status : 0x%08x\n",
- rsnd_mod_name(mod), status);
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
stop = true;
}
- if (is_tdm || is_tdm_split)
- stop |= rsnd_ssi_busif_err_status_clear(mod);
+ stop |= rsnd_ssiu_busif_err_status_clear(mod);
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out:
@@ -1128,8 +1019,36 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
name = is_play ? "rx" : "tx";
return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
- mod, name);
+ SSI_NAME, mod, name);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ssi_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ seq_printf(m, "clock: %s\n", rsnd_rdai_is_clk_master(rdai) ?
+ "provider" : "consumer");
+ seq_printf(m, "bit_clk_inv: %d\n", rdai->bit_clk_inv);
+ seq_printf(m, "frm_clk_inv: %d\n", rdai->frm_clk_inv);
+ seq_printf(m, "pin share: %d\n", __rsnd_ssi_is_pin_sharing(mod));
+ seq_printf(m, "can out clk: %d\n", rsnd_ssi_can_output_clk(mod));
+ seq_printf(m, "multi secondary: %d\n", rsnd_ssi_is_multi_secondary(mod, io));
+ seq_printf(m, "tdm: %d, %d\n", rsnd_runtime_is_tdm(io),
+ rsnd_runtime_is_tdm_split(io));
+ seq_printf(m, "chan: %d\n", ssi->chan);
+ seq_printf(m, "user: %d\n", ssi->usrcnt);
+
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SSI,
+ rsnd_mod_id(mod) * 0x40, 0x40);
}
+#define DEBUG_INFO .debug_info = rsnd_ssi_debug_info
+#else
+#define DEBUG_INFO
+#endif
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.name = SSI_NAME,
@@ -1145,9 +1064,10 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params,
.get_status = rsnd_ssi_get_status,
+ DEBUG_INFO
};
-static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
{
return mod->ops == &rsnd_ssi_dma_ops;
}
@@ -1195,7 +1115,11 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
i = 0;
for_each_child_of_node(node, np) {
- struct rsnd_mod *mod = rsnd_ssi_mod_get(priv, i);
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(np, SSI_NAME, i);
+
+ mod = rsnd_ssi_mod_get(priv, i);
if (np == playback)
rsnd_ssi_connect(mod, &rdai->playback);
@@ -1238,7 +1162,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
if (!node)
return -EINVAL;
- nr = of_get_child_count(node);
+ nr = rsnd_node_count(priv, node, SSI_NAME);
if (!nr) {
ret = -EINVAL;
goto rsnd_ssi_probe_done;
@@ -1258,6 +1182,8 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
if (!of_device_is_available(np))
goto skip;
+ i = rsnd_node_fixed_index(np, SSI_NAME, i);
+
ssi = rsnd_ssi_get(priv, i);
snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 852cdeedf7e9..0d8f97633dd2 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -45,6 +45,85 @@ struct rsnd_ssiu {
static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 };
static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
+/* enable busif buffer over/under run interrupt. */
+#define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1)
+#define rsnd_ssiu_busif_err_irq_disable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 0)
+static void rsnd_ssiu_busif_err_irq_ctrl(struct rsnd_mod *mod, int enable)
+{
+ int id = rsnd_mod_id(mod);
+ int shift, offset;
+ int i;
+
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ shift = id;
+ offset = 0;
+ break;
+ case 9:
+ shift = 1;
+ offset = 1;
+ break;
+ }
+
+ for (i = 0; i < 4; i++) {
+ enum rsnd_reg reg = SSI_SYS_INT_ENABLE((i * 2) + offset);
+ u32 val = 0xf << (shift * 4);
+ u32 sys_int_enable = rsnd_mod_read(mod, reg);
+
+ if (enable)
+ sys_int_enable |= val;
+ else
+ sys_int_enable &= ~val;
+ rsnd_mod_write(mod, reg, sys_int_enable);
+ }
+}
+
+bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod)
+{
+ bool error = false;
+ int id = rsnd_mod_id(mod);
+ int shift, offset;
+ int i;
+
+ switch (id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ shift = id;
+ offset = 0;
+ break;
+ case 9:
+ shift = 1;
+ offset = 1;
+ break;
+ }
+
+ for (i = 0; i < 4; i++) {
+ u32 reg = SSI_SYS_STATUS(i * 2) + offset;
+ u32 status = rsnd_mod_read(mod, reg);
+ u32 val = 0xf << (shift * 4);
+
+ status &= val;
+ if (status) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ rsnd_print_irq_status(dev, "%s err status : 0x%08x\n",
+ rsnd_mod_name(mod), status);
+ error = true;
+ }
+ rsnd_mod_write(mod, reg, val);
+ }
+
+ return error;
+}
+
static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
@@ -65,23 +144,9 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
int id = rsnd_mod_id(mod);
int is_clk_master = rsnd_rdai_is_clk_master(rdai);
u32 val1, val2;
- int i;
/* clear status */
- switch (id) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- for (i = 0; i < 4; i++)
- rsnd_mod_write(mod, SSI_SYS_STATUS(i * 2), 0xf << (id * 4));
- break;
- case 9:
- for (i = 0; i < 4; i++)
- rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << 4);
- break;
- }
+ rsnd_ssiu_busif_err_status_clear(mod);
/*
* SSI_MODE0
@@ -137,12 +202,31 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
+ /*
+ * Enable busif buffer over/under run interrupt.
+ * It will be handled from ssi.c
+ * see
+ * __rsnd_ssi_interrupt()
+ */
+ rsnd_ssiu_busif_err_irq_enable(mod);
+
+ return 0;
+}
+
+static int rsnd_ssiu_quit(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
+{
+ /* disable busif buffer over/under run interrupt. */
+ rsnd_ssiu_busif_err_irq_disable(mod);
+
return 0;
}
static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
.name = SSIU_NAME,
.init = rsnd_ssiu_init,
+ .quit = rsnd_ssiu_quit,
.get_status = rsnd_ssiu_get_status,
};
@@ -311,9 +395,22 @@ static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
name = is_play ? "rx" : "tx";
return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
- mod, name);
+ SSIU_NAME, mod, name);
}
+#ifdef CONFIG_DEBUG_FS
+static void rsnd_ssiu_debug_info(struct seq_file *m,
+ struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ rsnd_debugfs_mod_reg_show(m, mod, RSND_GEN2_SSIU,
+ rsnd_mod_id(mod) * 0x80, 0x80);
+}
+#define DEBUG_INFO .debug_info = rsnd_ssiu_debug_info
+#else
+#define DEBUG_INFO
+#endif
+
static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
.name = SSIU_NAME,
.dma_req = rsnd_ssiu_dma_req,
@@ -321,6 +418,7 @@ static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
.start = rsnd_ssiu_start_gen2,
.stop = rsnd_ssiu_stop_gen2,
.get_status = rsnd_ssiu_get_status,
+ DEBUG_INFO
};
static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
@@ -336,16 +434,20 @@ static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
{
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssiu *ssiu;
+ int is_dma_mode;
int i;
if (!ssi_mod)
return;
+ is_dma_mode = rsnd_ssi_is_dma_mode(ssi_mod);
+
/* select BUSIF0 */
for_each_rsnd_ssiu(ssiu, priv, i) {
struct rsnd_mod *mod = rsnd_mod_get(ssiu);
- if ((rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
+ if (is_dma_mode &&
+ (rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
(rsnd_mod_id_sub(mod) == 0)) {
rsnd_dai_connect(mod, io, mod->type);
return;
@@ -368,7 +470,11 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
int i = 0;
for_each_child_of_node(node, np) {
- struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, i);
+ struct rsnd_mod *mod;
+
+ i = rsnd_node_fixed_index(np, SSIU_NAME, i);
+
+ mod = rsnd_ssiu_mod_get(priv, i);
if (np == playback)
rsnd_dai_connect(mod, io_p, mod->type);
@@ -405,10 +511,13 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
*/
node = rsnd_ssiu_of_node(priv);
if (node)
- nr = of_get_child_count(node);
+ nr = rsnd_node_count(priv, node, SSIU_NAME);
else
nr = priv->ssi_nr;
+ if (!nr)
+ return -EINVAL;
+
ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
if (!ssiu)
return -ENOMEM;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 1c0904acb935..44e65f984a5c 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -75,9 +75,9 @@ static ssize_t pmdown_time_show(struct device *dev,
return sprintf(buf, "%ld\n", rtd->pmdown_time);
}
-static ssize_t pmdown_time_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t pmdown_time_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
int ret;
@@ -89,7 +89,7 @@ static ssize_t pmdown_time_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
+static DEVICE_ATTR_RW(pmdown_time);
static struct attribute *soc_dev_attrs[] = {
&dev_attr_pmdown_time.attr,
@@ -1054,6 +1054,231 @@ _err_defer:
}
EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime);
+static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ struct snd_soc_dai *dai, *not_used;
+ struct device *dev = rtd->dev;
+ u64 pos, possible_fmt;
+ unsigned int mask = 0, dai_fmt = 0;
+ int i, j, priority, pri, until;
+
+ /*
+ * Get selectable format from each DAIs.
+ *
+ ****************************
+ * NOTE
+ * Using .auto_selectable_formats is not mandatory,
+ * we can select format manually from Sound Card.
+ * When use it, driver should list well tested format only.
+ ****************************
+ *
+ * ex)
+ * auto_selectable_formats (= SND_SOC_POSSIBLE_xxx)
+ * (A) (B) (C)
+ * DAI0_: { 0x000F, 0x00F0, 0x0F00 };
+ * DAI1 : { 0xF000, 0x0F00 };
+ * (X) (Y)
+ *
+ * "until" will be 3 in this case (MAX array size from DAI0 and DAI1)
+ * Here is dev_dbg() message and comments
+ *
+ * priority = 1
+ * DAI0: (pri, fmt) = (1, 000000000000000F) // 1st check (A) DAI1 is not selected
+ * DAI1: (pri, fmt) = (0, 0000000000000000) // Necessary Waste
+ * DAI0: (pri, fmt) = (1, 000000000000000F) // 2nd check (A)
+ * DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
+ * priority = 2
+ * DAI0: (pri, fmt) = (2, 00000000000000FF) // 3rd check (A) + (B)
+ * DAI1: (pri, fmt) = (1, 000000000000F000) // (X)
+ * DAI0: (pri, fmt) = (2, 00000000000000FF) // 4th check (A) + (B)
+ * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
+ * priority = 3
+ * DAI0: (pri, fmt) = (3, 0000000000000FFF) // 5th check (A) + (B) + (C)
+ * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y)
+ * found auto selected format: 0000000000000F00
+ */
+ until = snd_soc_dai_get_fmt_max_priority(rtd);
+ for (priority = 1; priority <= until; priority++) {
+
+ dev_dbg(dev, "priority = %d\n", priority);
+ for_each_rtd_dais(rtd, j, not_used) {
+
+ possible_fmt = ULLONG_MAX;
+ for_each_rtd_dais(rtd, i, dai) {
+ u64 fmt = 0;
+
+ pri = (j >= i) ? priority : priority - 1;
+ fmt = snd_soc_dai_get_fmt(dai, pri);
+ dev_dbg(dev, "%s: (pri, fmt) = (%d, %016llX)\n", dai->name, pri, fmt);
+ possible_fmt &= fmt;
+ }
+ if (possible_fmt)
+ goto found;
+ }
+ }
+ /* Not Found */
+ return;
+found:
+ dev_dbg(dev, "found auto selected format: %016llX\n", possible_fmt);
+
+ /*
+ * convert POSSIBLE_DAIFMT to DAIFMT
+ *
+ * Some basic/default settings on each is defined as 0.
+ * see
+ * SND_SOC_DAIFMT_NB_NF
+ * SND_SOC_DAIFMT_GATED
+ *
+ * SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify
+ * these value, and will be overwrite to auto selected value.
+ *
+ * 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
+ * as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx.
+ */
+ for (i = 63; i >= 0; i--) {
+ pos = 1ULL << i;
+ switch (possible_fmt & pos) {
+ /*
+ * for format
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_I2S:
+ case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J:
+ case SND_SOC_POSSIBLE_DAIFMT_LEFT_J:
+ case SND_SOC_POSSIBLE_DAIFMT_DSP_A:
+ case SND_SOC_POSSIBLE_DAIFMT_DSP_B:
+ case SND_SOC_POSSIBLE_DAIFMT_AC97:
+ case SND_SOC_POSSIBLE_DAIFMT_PDM:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i;
+ break;
+ /*
+ * for clock
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_CONT:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_GATED:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED;
+ break;
+ /*
+ * for clock invert
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_NB_NF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_NB_IF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_IB_NF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_IB_IF:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF;
+ break;
+ /*
+ * for clock provider / consumer
+ */
+ case SND_SOC_POSSIBLE_DAIFMT_CBP_CFP:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBC_CFP:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFP;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBP_CFC:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFC;
+ break;
+ case SND_SOC_POSSIBLE_DAIFMT_CBC_CFC:
+ dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ }
+ }
+
+ /*
+ * Some driver might have very complex limitation.
+ * In such case, user want to auto-select non-limitation part,
+ * and want to manually specify complex part.
+ *
+ * Or for example, if both CPU and Codec can be clock provider,
+ * but because of its quality, user want to specify it manually.
+ *
+ * Use manually specified settings if sound card did.
+ */
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK))
+ mask |= SND_SOC_DAIFMT_FORMAT_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_MASK))
+ mask |= SND_SOC_DAIFMT_CLOCK_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_INV_MASK))
+ mask |= SND_SOC_DAIFMT_INV_MASK;
+ if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK))
+ mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+
+ dai_link->dai_fmt |= (dai_fmt & mask);
+}
+
+/**
+ * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime
+ * @rtd: The runtime for which the DAI link format should be changed
+ * @dai_fmt: The new DAI link format
+ *
+ * This function updates the DAI link format for all DAIs connected to the DAI
+ * link for the specified runtime.
+ *
+ * Note: For setups with a static format set the dai_fmt field in the
+ * corresponding snd_dai_link struct instead of using this function.
+ *
+ * Returns 0 on success, otherwise a negative error code.
+ */
+int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
+ unsigned int dai_fmt)
+{
+ struct snd_soc_dai *cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ unsigned int inv_dai_fmt;
+ unsigned int i;
+ int ret;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ return ret;
+ }
+
+ /*
+ * Flip the polarity for the "CPU" end of a CODEC<->CODEC link
+ * the component which has non_legacy_dai_naming is Codec
+ */
+ inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK;
+ switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ }
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ unsigned int fmt = dai_fmt;
+
+ if (cpu_dai->component->driver->non_legacy_dai_naming)
+ fmt = inv_dai_fmt;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
+
static int soc_init_pcm_runtime(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
{
@@ -1070,6 +1295,7 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card,
if (ret < 0)
return ret;
+ snd_soc_runtime_get_dai_fmt(rtd);
if (dai_link->dai_fmt) {
ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
if (ret)
@@ -1402,68 +1628,6 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
}
}
-/**
- * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime
- * @rtd: The runtime for which the DAI link format should be changed
- * @dai_fmt: The new DAI link format
- *
- * This function updates the DAI link format for all DAIs connected to the DAI
- * link for the specified runtime.
- *
- * Note: For setups with a static format set the dai_fmt field in the
- * corresponding snd_dai_link struct instead of using this function.
- *
- * Returns 0 on success, otherwise a negative error code.
- */
-int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
- unsigned int dai_fmt)
-{
- struct snd_soc_dai *cpu_dai;
- struct snd_soc_dai *codec_dai;
- unsigned int inv_dai_fmt;
- unsigned int i;
- int ret;
-
- for_each_rtd_codec_dais(rtd, i, codec_dai) {
- ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
- if (ret != 0 && ret != -ENOTSUPP)
- return ret;
- }
-
- /*
- * Flip the polarity for the "CPU" end of a CODEC<->CODEC link
- * the component which has non_legacy_dai_naming is Codec
- */
- inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK;
- switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
- break;
- }
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- unsigned int fmt = dai_fmt;
-
- if (cpu_dai->component->driver->non_legacy_dai_naming)
- fmt = inv_dai_fmt;
-
- ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
- if (ret != 0 && ret != -ENOTSUPP)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
-
#ifdef CONFIG_DMI
/*
* If a DMI filed contain strings in this blacklist (e.g.
@@ -2225,6 +2389,8 @@ static char *fmt_single_name(struct device *dev, int *id)
return NULL;
name = devm_kstrdup(dev, devname, GFP_KERNEL);
+ if (!name)
+ return NULL;
/* are we a "%s.%d" name (platform and SPI components) */
found = strstr(name, dev->driver->name);
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index 080fbe053fc5..a56dcc8d6fb7 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -134,6 +134,69 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
}
EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio);
+int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai;
+ int i, max = 0;
+
+ /*
+ * return max num if *ALL* DAIs have .auto_selectable_formats
+ */
+ for_each_rtd_dais(rtd, i, dai) {
+ if (dai->driver->ops &&
+ dai->driver->ops->num_auto_selectable_formats)
+ max = max(max, dai->driver->ops->num_auto_selectable_formats);
+ else
+ return 0;
+ }
+
+ return max;
+}
+
+/**
+ * snd_soc_dai_get_fmt - get supported audio format.
+ * @dai: DAI
+ * @priority: priority level of supported audio format.
+ *
+ * This should return only formats implemented with high
+ * quality by the DAI so that the core can configure a
+ * format which will work well with other devices.
+ * For example devices which don't support both edges of the
+ * LRCLK signal in I2S style formats should only list DSP
+ * modes. This will mean that sometimes fewer formats
+ * are reported here than are supported by set_fmt().
+ */
+u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority)
+{
+ const struct snd_soc_dai_ops *ops = dai->driver->ops;
+ u64 fmt = 0;
+ int i, max = 0, until = priority;
+
+ /*
+ * Collect auto_selectable_formats until priority
+ *
+ * ex)
+ * auto_selectable_formats[] = { A, B, C };
+ * (A, B, C = SND_SOC_POSSIBLE_DAIFMT_xxx)
+ *
+ * priority = 1 : A
+ * priority = 2 : A | B
+ * priority = 3 : A | B | C
+ * priority = 4 : A | B | C
+ * ...
+ */
+ if (ops)
+ max = ops->num_auto_selectable_formats;
+
+ if (max < until)
+ until = max;
+
+ for (i = 0; i < until; i++)
+ fmt |= ops->auto_selectable_formats[i];
+
+ return fmt;
+}
+
/**
* snd_soc_dai_set_fmt - configure DAI hardware audio format.
* @dai: DAI
@@ -327,14 +390,15 @@ int snd_soc_dai_hw_params(struct snd_soc_dai *dai,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
int ret = 0;
- /* perform any topology hw_params fixups before DAI */
- ret = snd_soc_link_be_hw_params_fixup(rtd, params);
- if (ret < 0)
- goto end;
-
if (dai->driver->ops &&
- dai->driver->ops->hw_params)
+ dai->driver->ops->hw_params) {
+ /* perform any topology hw_params fixups before DAI */
+ ret = snd_soc_link_be_hw_params_fixup(rtd, params);
+ if (ret < 0)
+ goto end;
+
ret = dai->driver->ops->hw_params(substream, params, dai);
+ }
/* mark substream if succeeded */
if (ret == 0)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 73076d425efb..5e65b72910e9 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1203,249 +1203,216 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
return ret;
}
-static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
- struct soc_tplg *tplg, int num_kcontrols)
+static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
{
- struct snd_kcontrol_new *kc;
struct soc_mixer_control *sm;
struct snd_soc_tplg_mixer_control *mc;
- int i, err;
-
- kc = devm_kcalloc(tplg->dev, num_kcontrols, sizeof(*kc), GFP_KERNEL);
- if (kc == NULL)
- return NULL;
-
- for (i = 0; i < num_kcontrols; i++) {
- mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
-
- /* validate kcontrol */
- if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err_sm;
+ int err;
- sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
- if (sm == NULL)
- goto err_sm;
+ mc = (struct snd_soc_tplg_mixer_control *)tplg->pos;
- tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- le32_to_cpu(mc->priv.size));
+ /* validate kcontrol */
+ if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
- mc->hdr.name, i);
+ sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL);
+ if (!sm)
+ return -ENOMEM;
- kc[i].private_value = (long)sm;
- kc[i].name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL);
- if (kc[i].name == NULL)
- goto err_sm;
- kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = le32_to_cpu(mc->hdr.access);
+ tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) +
+ le32_to_cpu(mc->priv.size);
- /* we only support FL/FR channel mapping atm */
- sm->reg = tplc_chan_get_reg(tplg, mc->channel,
- SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
- SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
- SNDRV_CHMAP_FR);
+ dev_dbg(tplg->dev, " adding DAPM widget mixer control %s\n",
+ mc->hdr.name);
- sm->max = le32_to_cpu(mc->max);
- sm->min = le32_to_cpu(mc->min);
- sm->invert = le32_to_cpu(mc->invert);
- sm->platform_max = le32_to_cpu(mc->platform_max);
- sm->dobj.index = tplg->index;
- INIT_LIST_HEAD(&sm->dobj.list);
-
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg);
- if (err) {
- soc_control_err(tplg, &mc->hdr, mc->hdr.name);
- goto err_sm;
- }
+ kc->private_value = (long)sm;
+ kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = le32_to_cpu(mc->hdr.access);
+
+ /* we only support FL/FR channel mapping atm */
+ sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+ sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FL);
+ sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+ SNDRV_CHMAP_FR);
+
+ sm->max = le32_to_cpu(mc->max);
+ sm->min = le32_to_cpu(mc->min);
+ sm->invert = le32_to_cpu(mc->invert);
+ sm->platform_max = le32_to_cpu(mc->platform_max);
+ sm->dobj.index = tplg->index;
+ INIT_LIST_HEAD(&sm->dobj.list);
+
+ /* map io handlers */
+ err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg);
+ if (err) {
+ soc_control_err(tplg, &mc->hdr, mc->hdr.name);
+ return err;
+ }
- /* create any TLV data */
- err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
- mc->hdr.name);
- goto err_sm;
- }
+ /* create any TLV data */
+ err = soc_tplg_create_tlv(tplg, kc, &mc->hdr);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+ mc->hdr.name);
+ return err;
+ }
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc[i],
- (struct snd_soc_tplg_ctl_hdr *)mc);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- mc->hdr.name);
- goto err_sm;
- }
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, kc,
+ (struct snd_soc_tplg_ctl_hdr *)mc);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ mc->hdr.name);
+ return err;
}
- return kc;
-err_sm:
- return NULL;
+ return 0;
}
-static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
- struct soc_tplg *tplg, int num_kcontrols)
+static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
{
- struct snd_kcontrol_new *kc;
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
- int i, err;
-
- kc = devm_kcalloc(tplg->dev, num_kcontrols, sizeof(*kc), GFP_KERNEL);
- if (kc == NULL)
- return NULL;
-
- for (i = 0; i < num_kcontrols; i++) {
- ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err_se;
+ int err;
- se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL);
- if (se == NULL)
- goto err_se;
+ ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+ /* validate kcontrol */
+ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- le32_to_cpu(ec->priv.size));
+ se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
- dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
- ec->hdr.name);
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ le32_to_cpu(ec->priv.size));
- kc[i].private_value = (long)se;
- kc[i].name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL);
- if (kc[i].name == NULL)
- goto err_se;
- kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = le32_to_cpu(ec->hdr.access);
+ dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
+ ec->hdr.name);
- /* we only support FL/FR channel mapping atm */
- se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
- se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
- SNDRV_CHMAP_FR);
+ kc->private_value = (long)se;
+ kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = le32_to_cpu(ec->hdr.access);
- se->items = le32_to_cpu(ec->items);
- se->mask = le32_to_cpu(ec->mask);
- se->dobj.index = tplg->index;
+ /* we only support FL/FR channel mapping atm */
+ se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FL);
+ se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+ SNDRV_CHMAP_FR);
- switch (le32_to_cpu(ec->hdr.ops.info)) {
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- err = soc_tplg_denum_create_values(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: could not create values for %s\n",
- ec->hdr.name);
- goto err_se;
- }
- fallthrough;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- err = soc_tplg_denum_create_texts(tplg, se, ec);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
- ec->hdr.name);
- goto err_se;
- }
- break;
- default:
- dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
- ec->hdr.ops.info, ec->hdr.name);
- goto err_se;
- }
+ se->items = le32_to_cpu(ec->items);
+ se->mask = le32_to_cpu(ec->mask);
+ se->dobj.index = tplg->index;
- /* map io handlers */
- err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg);
- if (err) {
- soc_control_err(tplg, &ec->hdr, ec->hdr.name);
- goto err_se;
+ switch (le32_to_cpu(ec->hdr.ops.info)) {
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ err = soc_tplg_denum_create_values(tplg, se, ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: could not create values for %s\n",
+ ec->hdr.name);
+ return err;
}
-
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc[i],
- (struct snd_soc_tplg_ctl_hdr *)ec);
+ fallthrough;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ err = soc_tplg_denum_create_texts(tplg, se, ec);
if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
ec->hdr.name);
- goto err_se;
+ return err;
}
+ break;
+ default:
+ dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
+ ec->hdr.ops.info, ec->hdr.name);
+ return -EINVAL;
}
- return kc;
+ /* map io handlers */
+ err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg);
+ if (err) {
+ soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+ return err;
+ }
-err_se:
- return NULL;
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, kc,
+ (struct snd_soc_tplg_ctl_hdr *)ec);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ ec->hdr.name);
+ return err;
+ }
+
+ return 0;
}
-static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create(
- struct soc_tplg *tplg, int num_kcontrols)
+static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc)
{
struct snd_soc_tplg_bytes_control *be;
struct soc_bytes_ext *sbe;
- struct snd_kcontrol_new *kc;
- int i, err;
-
- kc = devm_kcalloc(tplg->dev, num_kcontrols, sizeof(*kc), GFP_KERNEL);
- if (!kc)
- return NULL;
+ int err;
- for (i = 0; i < num_kcontrols; i++) {
- be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
+ be = (struct snd_soc_tplg_bytes_control *)tplg->pos;
- /* validate kcontrol */
- if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
- goto err_sbe;
+ /* validate kcontrol */
+ if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+ return -EINVAL;
- sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
- if (sbe == NULL)
- goto err_sbe;
+ sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL);
+ if (!sbe)
+ return -ENOMEM;
- tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
- le32_to_cpu(be->priv.size));
+ tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) +
+ le32_to_cpu(be->priv.size));
- dev_dbg(tplg->dev,
- "ASoC: adding bytes kcontrol %s with access 0x%x\n",
- be->hdr.name, be->hdr.access);
+ dev_dbg(tplg->dev,
+ "ASoC: adding bytes kcontrol %s with access 0x%x\n",
+ be->hdr.name, be->hdr.access);
- kc[i].private_value = (long)sbe;
- kc[i].name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL);
- if (kc[i].name == NULL)
- goto err_sbe;
- kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = le32_to_cpu(be->hdr.access);
+ kc->private_value = (long)sbe;
+ kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = le32_to_cpu(be->hdr.access);
- sbe->max = le32_to_cpu(be->max);
- INIT_LIST_HEAD(&sbe->dobj.list);
+ sbe->max = le32_to_cpu(be->max);
+ INIT_LIST_HEAD(&sbe->dobj.list);
- /* map standard io handlers and check for external handlers */
- err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg);
- if (err) {
- soc_control_err(tplg, &be->hdr, be->hdr.name);
- goto err_sbe;
- }
-
- /* pass control to driver for optional further init */
- err = soc_tplg_init_kcontrol(tplg, &kc[i],
- (struct snd_soc_tplg_ctl_hdr *)be);
- if (err < 0) {
- dev_err(tplg->dev, "ASoC: failed to init %s\n",
- be->hdr.name);
- goto err_sbe;
- }
+ /* map standard io handlers and check for external handlers */
+ err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg);
+ if (err) {
+ soc_control_err(tplg, &be->hdr, be->hdr.name);
+ return err;
}
- return kc;
-
-err_sbe:
+ /* pass control to driver for optional further init */
+ err = soc_tplg_init_kcontrol(tplg, kc,
+ (struct snd_soc_tplg_ctl_hdr *)be);
+ if (err < 0) {
+ dev_err(tplg->dev, "ASoC: failed to init %s\n",
+ be->hdr.name);
+ return err;
+ }
- return NULL;
+ return 0;
}
static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
@@ -1455,8 +1422,13 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
struct snd_soc_dapm_widget template, *widget;
struct snd_soc_tplg_ctl_hdr *control_hdr;
struct snd_soc_card *card = tplg->comp->card;
- unsigned int kcontrol_type;
+ unsigned int *kcontrol_type = NULL;
+ struct snd_kcontrol_new *kc;
+ int mixer_count = 0;
+ int bytes_count = 0;
+ int enum_count = 0;
int ret = 0;
+ int i;
if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
@@ -1499,7 +1471,6 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
le32_to_cpu(w->priv.size));
if (w->num_kcontrols == 0) {
- kcontrol_type = 0;
template.num_kcontrols = 0;
goto widget;
}
@@ -1508,57 +1479,66 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n",
w->name, w->num_kcontrols, control_hdr->type);
- switch (le32_to_cpu(control_hdr->ops.info)) {
- case SND_SOC_TPLG_CTL_VOLSW:
- case SND_SOC_TPLG_CTL_STROBE:
- case SND_SOC_TPLG_CTL_VOLSW_SX:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_DAPM_CTL_VOLSW:
- kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */
- template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
- template.kcontrol_news =
- soc_tplg_dapm_widget_dmixer_create(tplg,
- template.num_kcontrols);
- if (!template.kcontrol_news) {
- ret = -ENOMEM;
- goto hdr_err;
- }
- break;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
- kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */
- template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
- template.kcontrol_news =
- soc_tplg_dapm_widget_denum_create(tplg,
- template.num_kcontrols);
- if (!template.kcontrol_news) {
- ret = -ENOMEM;
- goto hdr_err;
- }
- break;
- case SND_SOC_TPLG_CTL_BYTES:
- kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */
- template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
- template.kcontrol_news =
- soc_tplg_dapm_widget_dbytes_create(tplg,
- template.num_kcontrols);
- if (!template.kcontrol_news) {
- ret = -ENOMEM;
+ template.num_kcontrols = le32_to_cpu(w->num_kcontrols);
+ kc = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(*kc), GFP_KERNEL);
+ if (!kc)
+ goto err;
+
+ kcontrol_type = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!kcontrol_type)
+ goto err;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
+ switch (le32_to_cpu(control_hdr->ops.info)) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_STROBE:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ case SND_SOC_TPLG_CTL_RANGE:
+ case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+ /* volume mixer */
+ kc[i].index = mixer_count;
+ kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER;
+ mixer_count++;
+ ret = soc_tplg_dapm_widget_dmixer_create(tplg, &kc[i]);
+ if (ret < 0)
+ goto hdr_err;
+ break;
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+ case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ /* enumerated mixer */
+ kc[i].index = enum_count;
+ kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM;
+ enum_count++;
+ ret = soc_tplg_dapm_widget_denum_create(tplg, &kc[i]);
+ if (ret < 0)
+ goto hdr_err;
+ break;
+ case SND_SOC_TPLG_CTL_BYTES:
+ /* bytes control */
+ kc[i].index = bytes_count;
+ kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES;
+ bytes_count++;
+ ret = soc_tplg_dapm_widget_dbytes_create(tplg, &kc[i]);
+ if (ret < 0)
+ goto hdr_err;
+ break;
+ default:
+ dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n",
+ control_hdr->ops.get, control_hdr->ops.put,
+ le32_to_cpu(control_hdr->ops.info));
+ ret = -EINVAL;
goto hdr_err;
}
- break;
- default:
- dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n",
- control_hdr->ops.get, control_hdr->ops.put,
- le32_to_cpu(control_hdr->ops.info));
- ret = -EINVAL;
- goto hdr_err;
}
+ template.kcontrol_news = kc;
+
widget:
ret = soc_tplg_widget_load(tplg, &template, w);
if (ret < 0)
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 98383fd76224..299b5d6ebfd1 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -97,6 +97,34 @@ static const struct snd_soc_component_driver dummy_codec = {
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+/*
+ * Select these from Sound Card Manually
+ * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP
+ * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC
+ * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP
+ * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC
+ */
+static u64 dummy_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_AC97 |
+ SND_SOC_POSSIBLE_DAIFMT_PDM |
+ SND_SOC_POSSIBLE_DAIFMT_GATED |
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const struct snd_soc_dai_ops dummy_dai_ops = {
+ .auto_selectable_formats = &dummy_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
/*
* The dummy CODEC is only meant to be used in situations where there is no
* actual hardware.
@@ -122,6 +150,7 @@ static struct snd_soc_dai_driver dummy_dai = {
.rates = STUB_RATES,
.formats = STUB_FORMATS,
},
+ .ops = &dummy_dai_ops,
};
int snd_soc_dai_is_dummy(struct snd_soc_dai *dai)
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 2d4969c705a4..57d5bf0a171e 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -13,7 +13,7 @@
#include "ops.h"
#include "probe.h"
-struct snd_compress_ops sof_probe_compressed_ops = {
+const struct snd_compress_ops sof_probe_compressed_ops = {
.copy = sof_probe_compr_copy,
};
EXPORT_SYMBOL(sof_probe_compressed_ops);
diff --git a/sound/soc/sof/compress.h b/sound/soc/sof/compress.h
index ca8790bd4b13..4448c799e14b 100644
--- a/sound/soc/sof/compress.h
+++ b/sound/soc/sof/compress.h
@@ -13,7 +13,7 @@
#include <sound/compress_driver.h>
-extern struct snd_compress_ops sof_probe_compressed_ops;
+extern const struct snd_compress_ops sof_probe_compressed_ops;
int sof_probe_compr_open(struct snd_compr_stream *cstream,
struct snd_soc_dai *dai);
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index f3d6f7070fb3..feae487f0227 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -13,7 +13,10 @@ snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o
snd-sof-intel-hda-objs := hda-codec.o
-obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-acpi-intel-byt.o
+snd-sof-intel-atom-objs := atom.o
+
+obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o
+obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o
obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o
obj-$(CONFIG_SND_SOC_SOF_INTEL_HIFI_EP_IPC) += snd-sof-intel-ipc.o
obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c
new file mode 100644
index 000000000000..d8804efede5e
--- /dev/null
+++ b/sound/soc/sof/intel/atom.c
@@ -0,0 +1,463 @@
+// 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) 2018-2021 Intel Corporation. All rights reserved.
+//
+// Author: Liam Girdwood <[email protected]>
+//
+
+/*
+ * Hardware interface for audio DSP on Atom devices
+ */
+
+#include <linux/module.h>
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/intel-dsp-config.h>
+#include "../ops.h"
+#include "shim.h"
+#include "atom.h"
+#include "../sof-acpi-dev.h"
+#include "../sof-audio.h"
+#include "../../intel/common/soc-intel-quirks.h"
+
+static void atom_host_done(struct snd_sof_dev *sdev);
+static void atom_dsp_done(struct snd_sof_dev *sdev);
+static void atom_get_reply(struct snd_sof_dev *sdev);
+
+/*
+ * Debug
+ */
+
+static void atom_get_registers(struct snd_sof_dev *sdev,
+ struct sof_ipc_dsp_oops_xtensa *xoops,
+ struct sof_ipc_panic_info *panic_info,
+ u32 *stack, size_t stack_words)
+{
+ u32 offset = sdev->dsp_oops_offset;
+
+ /* first read regsisters */
+ sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+
+ /* note: variable AR register array is not read */
+
+ /* then get panic info */
+ if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
+ dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
+ xoops->arch_hdr.totalsize);
+ return;
+ }
+ offset += xoops->arch_hdr.totalsize;
+ sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
+
+ /* then get the stack */
+ offset += sizeof(*panic_info);
+ sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
+}
+
+void atom_dump(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc_dsp_oops_xtensa xoops;
+ struct sof_ipc_panic_info panic_info;
+ u32 stack[STACK_DUMP_SIZE];
+ u64 status, panic, imrd, imrx;
+
+ /* now try generic SOF status messages */
+ status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+ panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ atom_get_registers(sdev, &xoops, &panic_info, stack,
+ STACK_DUMP_SIZE);
+ snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
+ STACK_DUMP_SIZE);
+
+ /* provide some context for firmware debug */
+ imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
+ imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);
+ dev_err(sdev->dev,
+ "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
+ (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
+ (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
+ dev_err(sdev->dev,
+ "error: mask host: pending %s complete %s raw 0x%llx\n",
+ (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
+ (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
+ dev_err(sdev->dev,
+ "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
+ (status & SHIM_IPCD_BUSY) ? "yes" : "no",
+ (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
+ dev_err(sdev->dev,
+ "error: mask DSP: pending %s complete %s raw 0x%llx\n",
+ (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
+ (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
+
+}
+EXPORT_SYMBOL_NS(atom_dump, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+/*
+ * IPC Doorbell IRQ handler and thread.
+ */
+
+irqreturn_t atom_irq_handler(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ u64 ipcx, ipcd;
+ int ret = IRQ_NONE;
+
+ ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
+
+ /* reply message from DSP, Mask Done interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
+
+ /* new message from DSP, Mask Busy interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_BUSY,
+ SHIM_IMRX_BUSY);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(atom_irq_handler, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+irqreturn_t atom_irq_thread(int irq, void *context)
+{
+ struct snd_sof_dev *sdev = context;
+ u64 ipcx, ipcd;
+
+ ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
+
+ /* reply message from DSP */
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
+
+ spin_lock_irq(&sdev->ipc_lock);
+
+ /*
+ * handle immediate reply from DSP core. If the msg is
+ * found, set done bit in cmd_done which is called at the
+ * end of message processing function, else set it here
+ * because the done bit can't be set in cmd_done function
+ * which is triggered by msg
+ */
+ atom_get_reply(sdev);
+ snd_sof_ipc_reply(sdev, ipcx);
+
+ atom_dsp_done(sdev);
+
+ spin_unlock_irq(&sdev->ipc_lock);
+ }
+
+ /* new message from DSP */
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
+
+ /* Handle messages from DSP Core */
+ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) +
+ MBOX_OFFSET);
+ } else {
+ snd_sof_ipc_msgs_rx(sdev);
+ }
+
+ atom_host_done(sdev);
+ }
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_NS(atom_irq_thread, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ /* unmask and prepare to receive Done interrupt */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_DONE, 0);
+
+ /* send the message */
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+ snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+static void atom_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ /*
+ * Sometimes, there is unexpected reply ipc arriving. The reply
+ * ipc belongs to none of the ipcs sent from driver.
+ * In this case, the driver must ignore the ipc.
+ */
+ if (!msg) {
+ dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
+ return;
+ }
+
+ /* get reply */
+ sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
+
+ if (reply.error < 0) {
+ memcpy(msg->reply_data, &reply, sizeof(reply));
+ ret = reply.error;
+ } else {
+ /* reply correct size ? */
+ if (reply.hdr.size != msg->reply_size) {
+ dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
+ msg->reply_size, reply.hdr.size);
+ ret = -EINVAL;
+ }
+
+ /* read the message */
+ if (msg->reply_size > 0)
+ sof_mailbox_read(sdev, sdev->host_box.offset,
+ msg->reply_data, msg->reply_size);
+ }
+
+ msg->reply_error = ret;
+}
+
+int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+EXPORT_SYMBOL_NS(atom_get_mailbox_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+EXPORT_SYMBOL_NS(atom_get_window_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+static void atom_host_done(struct snd_sof_dev *sdev)
+{
+ /* clear BUSY bit and set DONE bit - accept new messages */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,
+ SHIM_BYT_IPCD_BUSY |
+ SHIM_BYT_IPCD_DONE,
+ SHIM_BYT_IPCD_DONE);
+
+ /* unmask and prepare to receive next new message */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY, 0);
+}
+
+static void atom_dsp_done(struct snd_sof_dev *sdev)
+{
+ /* clear DONE bit - tell DSP we have completed */
+ snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,
+ SHIM_BYT_IPCX_DONE, 0);
+}
+
+/*
+ * DSP control.
+ */
+
+int atom_run(struct snd_sof_dev *sdev)
+{
+ int tries = 10;
+
+ /* release stall and wait to unstall */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_STALL, 0x0);
+ while (tries--) {
+ if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &
+ SHIM_BYT_CSR_PWAITMODE))
+ break;
+ msleep(100);
+ }
+ if (tries < 0) {
+ dev_err(sdev->dev, "error: unable to run DSP firmware\n");
+ atom_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
+ return -ENODEV;
+ }
+
+ /* return init core mask */
+ return 1;
+}
+EXPORT_SYMBOL_NS(atom_run, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+int atom_reset(struct snd_sof_dev *sdev)
+{
+ /* put DSP into reset, set reset vector and stall */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
+ SHIM_BYT_CSR_STALL,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
+ SHIM_BYT_CSR_STALL);
+
+ usleep_range(10, 15);
+
+ /* take DSP out of reset and keep stalled for FW loading */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_RST, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(atom_reset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
+ const char *sof_tplg_filename,
+ const char *ssp_str)
+{
+ const char *tplg_filename = NULL;
+ char *filename;
+ char *split_ext;
+
+ filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL);
+ if (!filename)
+ return NULL;
+
+ /* this assumes a .tplg extension */
+ split_ext = strsep(&filename, ".");
+ if (split_ext) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s-%s.tplg",
+ split_ext, ssp_str);
+ if (!tplg_filename)
+ return NULL;
+ }
+ return tplg_filename;
+}
+
+void atom_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_soc_acpi_mach *mach;
+ struct platform_device *pdev;
+ const char *tplg_filename;
+
+ mach = snd_soc_acpi_find_machine(desc->machines);
+ if (!mach) {
+ dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
+ return;
+ }
+
+ pdev = to_platform_device(sdev->dev);
+ if (soc_intel_is_byt_cr(pdev)) {
+ dev_dbg(sdev->dev,
+ "BYT-CR detected, SSP0 used instead of SSP2\n");
+
+ tplg_filename = fixup_tplg_name(sdev,
+ mach->sof_tplg_filename,
+ "ssp0");
+ } else {
+ tplg_filename = mach->sof_tplg_filename;
+ }
+
+ if (!tplg_filename) {
+ dev_dbg(sdev->dev,
+ "error: no topology filename\n");
+ return;
+ }
+
+ sof_pdata->tplg_filename = tplg_filename;
+ mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
+ sof_pdata->machine = mach;
+}
+EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+/* Atom DAIs */
+struct snd_soc_dai_driver atom_dai[] = {
+{
+ .name = "ssp0-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp1-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp2-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ }
+},
+{
+ .name = "ssp3-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp4-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+{
+ .name = "ssp5-port",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+},
+};
+EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+void atom_set_mach_params(const struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
+ struct snd_soc_acpi_mach_params *mach_params;
+
+ mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
+ mach_params->platform = dev_name(sdev->dev);
+ mach_params->num_dai_drivers = desc->ops->num_drv;
+ mach_params->dai_drivers = desc->ops->drv;
+}
+EXPORT_SYMBOL_NS(atom_set_mach_params, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h
new file mode 100644
index 000000000000..96a462c7a2e5
--- /dev/null
+++ b/sound/soc/sof/intel/atom.h
@@ -0,0 +1,74 @@
+/* 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) 2017-2021 Intel Corporation. All rights reserved.
+ *
+ * Author: Liam Girdwood <[email protected]>
+ */
+
+#ifndef __SOF_INTEL_ATOM_H
+#define __SOF_INTEL_ATOM_H
+
+/* DSP memories */
+#define IRAM_OFFSET 0x0C0000
+#define IRAM_SIZE (80 * 1024)
+#define DRAM_OFFSET 0x100000
+#define DRAM_SIZE (160 * 1024)
+#define SHIM_OFFSET 0x140000
+#define SHIM_SIZE_BYT 0x100
+#define SHIM_SIZE_CHT 0x118
+#define MBOX_OFFSET 0x144000
+#define MBOX_SIZE 0x1000
+#define EXCEPT_OFFSET 0x800
+#define EXCEPT_MAX_HDR_SIZE 0x400
+
+/* DSP peripherals */
+#define DMAC0_OFFSET 0x098000
+#define DMAC1_OFFSET 0x09c000
+#define DMAC2_OFFSET 0x094000
+#define DMAC_SIZE 0x420
+#define SSP0_OFFSET 0x0a0000
+#define SSP1_OFFSET 0x0a1000
+#define SSP2_OFFSET 0x0a2000
+#define SSP3_OFFSET 0x0a4000
+#define SSP4_OFFSET 0x0a5000
+#define SSP5_OFFSET 0x0a6000
+#define SSP_SIZE 0x100
+
+#define STACK_DUMP_SIZE 32
+
+#define PCI_BAR_SIZE 0x200000
+
+#define PANIC_OFFSET(x) (((x) & GENMASK_ULL(47, 32)) >> 32)
+
+/*
+ * Debug
+ */
+
+#define MBOX_DUMP_SIZE 0x30
+
+/* BARs */
+#define DSP_BAR 0
+#define PCI_BAR 1
+#define IMR_BAR 2
+
+irqreturn_t atom_irq_handler(int irq, void *context);
+irqreturn_t atom_irq_thread(int irq, void *context);
+
+int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
+int atom_get_mailbox_offset(struct snd_sof_dev *sdev);
+int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id);
+
+int atom_run(struct snd_sof_dev *sdev);
+int atom_reset(struct snd_sof_dev *sdev);
+void atom_dump(struct snd_sof_dev *sdev, u32 flags);
+
+void atom_machine_select(struct snd_sof_dev *sdev);
+void atom_set_mach_params(const struct snd_soc_acpi_mach *mach,
+ struct snd_sof_dev *sdev);
+
+extern struct snd_soc_dai_driver atom_dai[];
+
+#endif
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index d9803e2ba67f..8edaf6fdd218 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -19,662 +19,66 @@
#include <sound/soc-acpi-intel-match.h>
#include <sound/intel-dsp-config.h>
#include "../ops.h"
+#include "atom.h"
#include "shim.h"
#include "../sof-acpi-dev.h"
#include "../sof-audio.h"
#include "../../intel/common/soc-intel-quirks.h"
-/* DSP memories */
-#define IRAM_OFFSET 0x0C0000
-#define IRAM_SIZE (80 * 1024)
-#define DRAM_OFFSET 0x100000
-#define DRAM_SIZE (160 * 1024)
-#define SHIM_OFFSET 0x140000
-#define SHIM_SIZE_BYT 0x100
-#define SHIM_SIZE_CHT 0x118
-#define MBOX_OFFSET 0x144000
-#define MBOX_SIZE 0x1000
-#define EXCEPT_OFFSET 0x800
-#define EXCEPT_MAX_HDR_SIZE 0x400
-
-/* DSP peripherals */
-#define DMAC0_OFFSET 0x098000
-#define DMAC1_OFFSET 0x09c000
-#define DMAC2_OFFSET 0x094000
-#define DMAC_SIZE 0x420
-#define SSP0_OFFSET 0x0a0000
-#define SSP1_OFFSET 0x0a1000
-#define SSP2_OFFSET 0x0a2000
-#define SSP3_OFFSET 0x0a4000
-#define SSP4_OFFSET 0x0a5000
-#define SSP5_OFFSET 0x0a6000
-#define SSP_SIZE 0x100
-
-#define BYT_STACK_DUMP_SIZE 32
-
-#define BYT_PCI_BAR_SIZE 0x200000
-
-#define BYT_PANIC_OFFSET(x) (((x) & GENMASK_ULL(47, 32)) >> 32)
-
-/*
- * Debug
- */
-
-#define MBOX_DUMP_SIZE 0x30
-
-/* BARs */
-#define BYT_DSP_BAR 0
-#define BYT_PCI_BAR 1
-#define BYT_IMR_BAR 2
-
static const struct snd_sof_debugfs_map byt_debugfs[] = {
- {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
SOF_DEBUGFS_ACCESS_ALWAYS},
- {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
SOF_DEBUGFS_ACCESS_ALWAYS},
};
-static void byt_host_done(struct snd_sof_dev *sdev);
-static void byt_dsp_done(struct snd_sof_dev *sdev);
-static void byt_get_reply(struct snd_sof_dev *sdev);
-
-/*
- * Debug
- */
-
-static void byt_get_registers(struct snd_sof_dev *sdev,
- struct sof_ipc_dsp_oops_xtensa *xoops,
- struct sof_ipc_panic_info *panic_info,
- u32 *stack, size_t stack_words)
-{
- u32 offset = sdev->dsp_oops_offset;
-
- /* first read regsisters */
- sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
-
- /* note: variable AR register array is not read */
-
- /* then get panic info */
- if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
- dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
- xoops->arch_hdr.totalsize);
- return;
- }
- offset += xoops->arch_hdr.totalsize;
- sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
-
- /* then get the stack */
- offset += sizeof(*panic_info);
- sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
-}
-
-static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
-{
- struct sof_ipc_dsp_oops_xtensa xoops;
- struct sof_ipc_panic_info panic_info;
- u32 stack[BYT_STACK_DUMP_SIZE];
- u64 status, panic, imrd, imrx;
-
- /* now try generic SOF status messages */
- status = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
- panic = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- byt_get_registers(sdev, &xoops, &panic_info, stack,
- BYT_STACK_DUMP_SIZE);
- snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
- BYT_STACK_DUMP_SIZE);
-
- /* provide some context for firmware debug */
- imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX);
- imrd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRD);
- dev_err(sdev->dev,
- "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
- (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
- (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
- dev_err(sdev->dev,
- "error: mask host: pending %s complete %s raw 0x%llx\n",
- (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
- (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
- dev_err(sdev->dev,
- "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
- (status & SHIM_IPCD_BUSY) ? "yes" : "no",
- (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
- dev_err(sdev->dev,
- "error: mask DSP: pending %s complete %s raw 0x%llx\n",
- (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
- (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
-
-}
-
-/*
- * IPC Doorbell IRQ handler and thread.
- */
-
-static irqreturn_t byt_irq_handler(int irq, void *context)
-{
- struct snd_sof_dev *sdev = context;
- u64 ipcx, ipcd;
- int ret = IRQ_NONE;
-
- ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
-
- if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- /* reply message from DSP, Mask Done interrupt first */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_DONE,
- SHIM_IMRX_DONE);
- ret = IRQ_WAKE_THREAD;
- }
-
- if (ipcd & SHIM_BYT_IPCD_BUSY) {
-
- /* new message from DSP, Mask Busy interrupt first */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_BUSY,
- SHIM_IMRX_BUSY);
- ret = IRQ_WAKE_THREAD;
- }
-
- return ret;
-}
-
-static irqreturn_t byt_irq_thread(int irq, void *context)
-{
- struct snd_sof_dev *sdev = context;
- u64 ipcx, ipcd;
-
- ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
- ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
-
- /* reply message from DSP */
- if (ipcx & SHIM_BYT_IPCX_DONE) {
-
- spin_lock_irq(&sdev->ipc_lock);
-
- /*
- * handle immediate reply from DSP core. If the msg is
- * found, set done bit in cmd_done which is called at the
- * end of message processing function, else set it here
- * because the done bit can't be set in cmd_done function
- * which is triggered by msg
- */
- byt_get_reply(sdev);
- snd_sof_ipc_reply(sdev, ipcx);
-
- byt_dsp_done(sdev);
-
- spin_unlock_irq(&sdev->ipc_lock);
- }
-
- /* new message from DSP */
- if (ipcd & SHIM_BYT_IPCD_BUSY) {
-
- /* Handle messages from DSP Core */
- if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
- snd_sof_dsp_panic(sdev, BYT_PANIC_OFFSET(ipcd) +
- MBOX_OFFSET);
- } else {
- snd_sof_ipc_msgs_rx(sdev);
- }
-
- byt_host_done(sdev);
- }
-
- return IRQ_HANDLED;
-}
-
-static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
-{
- /* unmask and prepare to receive Done interrupt */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_DONE, 0);
-
- /* send the message */
- sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
- msg->msg_size);
- snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
-
- return 0;
-}
-
-static void byt_get_reply(struct snd_sof_dev *sdev)
-{
- struct snd_sof_ipc_msg *msg = sdev->msg;
- struct sof_ipc_reply reply;
- int ret = 0;
-
- /*
- * Sometimes, there is unexpected reply ipc arriving. The reply
- * ipc belongs to none of the ipcs sent from driver.
- * In this case, the driver must ignore the ipc.
- */
- if (!msg) {
- dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
- return;
- }
-
- /* get reply */
- sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
-
- if (reply.error < 0) {
- memcpy(msg->reply_data, &reply, sizeof(reply));
- ret = reply.error;
- } else {
- /* reply correct size ? */
- if (reply.hdr.size != msg->reply_size) {
- dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
- msg->reply_size, reply.hdr.size);
- ret = -EINVAL;
- }
-
- /* read the message */
- if (msg->reply_size > 0)
- sof_mailbox_read(sdev, sdev->host_box.offset,
- msg->reply_data, msg->reply_size);
- }
-
- msg->reply_error = ret;
-}
-
-static int byt_get_mailbox_offset(struct snd_sof_dev *sdev)
-{
- return MBOX_OFFSET;
-}
-
-static int byt_get_window_offset(struct snd_sof_dev *sdev, u32 id)
-{
- return MBOX_OFFSET;
-}
-
-static void byt_host_done(struct snd_sof_dev *sdev)
-{
- /* clear BUSY bit and set DONE bit - accept new messages */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCD,
- SHIM_BYT_IPCD_BUSY |
- SHIM_BYT_IPCD_DONE,
- SHIM_BYT_IPCD_DONE);
-
- /* unmask and prepare to receive next new message */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_BUSY, 0);
-}
-
-static void byt_dsp_done(struct snd_sof_dev *sdev)
-{
- /* clear DONE bit - tell DSP we have completed */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX,
- SHIM_BYT_IPCX_DONE, 0);
-}
-
-/*
- * DSP control.
- */
-
-static int byt_run(struct snd_sof_dev *sdev)
-{
- int tries = 10;
-
- /* release stall and wait to unstall */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_STALL, 0x0);
- while (tries--) {
- if (!(snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_CSR) &
- SHIM_BYT_CSR_PWAITMODE))
- break;
- msleep(100);
- }
- if (tries < 0) {
- dev_err(sdev->dev, "error: unable to run DSP firmware\n");
- byt_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
- return -ENODEV;
- }
-
- /* return init core mask */
- return 1;
-}
-
-static int byt_reset(struct snd_sof_dev *sdev)
-{
- /* put DSP into reset, set reset vector and stall */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
- SHIM_BYT_CSR_STALL,
- SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
- SHIM_BYT_CSR_STALL);
-
- usleep_range(10, 15);
-
- /* take DSP out of reset and keep stalled for FW loading */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
- SHIM_BYT_CSR_RST, 0);
-
- return 0;
-}
-
-static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
- const char *sof_tplg_filename,
- const char *ssp_str)
-{
- const char *tplg_filename = NULL;
- char *filename;
- char *split_ext;
-
- filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL);
- if (!filename)
- return NULL;
-
- /* this assumes a .tplg extension */
- split_ext = strsep(&filename, ".");
- if (split_ext) {
- tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
- "%s-%s.tplg",
- split_ext, ssp_str);
- if (!tplg_filename)
- return NULL;
- }
- return tplg_filename;
-}
-
-static void byt_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_soc_acpi_mach *mach;
- struct platform_device *pdev;
- const char *tplg_filename;
-
- mach = snd_soc_acpi_find_machine(desc->machines);
- if (!mach) {
- dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
- return;
- }
-
- pdev = to_platform_device(sdev->dev);
- if (soc_intel_is_byt_cr(pdev)) {
- dev_dbg(sdev->dev,
- "BYT-CR detected, SSP0 used instead of SSP2\n");
-
- tplg_filename = fixup_tplg_name(sdev,
- mach->sof_tplg_filename,
- "ssp0");
- } else {
- tplg_filename = mach->sof_tplg_filename;
- }
-
- if (!tplg_filename) {
- dev_dbg(sdev->dev,
- "error: no topology filename\n");
- return;
- }
-
- sof_pdata->tplg_filename = tplg_filename;
- mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
- sof_pdata->machine = mach;
-}
-
-/* Baytrail DAIs */
-static struct snd_soc_dai_driver byt_dai[] = {
-{
- .name = "ssp0-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp1-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp2-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- }
-},
-{
- .name = "ssp3-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp4-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "ssp5-port",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-};
-
-static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach,
- struct snd_sof_dev *sdev)
-{
- struct snd_sof_pdata *pdata = sdev->pdata;
- const struct sof_dev_desc *desc = pdata->desc;
- struct snd_soc_acpi_mach_params *mach_params;
-
- mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
- mach_params->platform = dev_name(sdev->dev);
- mach_params->num_dai_drivers = desc->ops->num_drv;
- mach_params->dai_drivers = desc->ops->drv;
-}
-
-/*
- * Probe and remove.
- */
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
-
-static int tangier_pci_probe(struct snd_sof_dev *sdev)
-{
- struct snd_sof_pdata *pdata = sdev->pdata;
- const struct sof_dev_desc *desc = pdata->desc;
- struct pci_dev *pci = to_pci_dev(sdev->dev);
- u32 base, size;
- int ret;
-
- /* DSP DMA can only access low 31 bits of host memory */
- ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret);
- return ret;
- }
-
- /* LPE base */
- base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
- size = BYT_PCI_BAR_SIZE;
-
- dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_DSP_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_DSP_BAR]) {
- dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
- base, size);
- return -ENODEV;
- }
- dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]);
-
- /* IMR base - optional */
- if (desc->resindex_imr_base == -1)
- goto irq;
-
- base = pci_resource_start(pci, desc->resindex_imr_base);
- size = pci_resource_len(pci, desc->resindex_imr_base);
-
- /* some BIOSes don't map IMR */
- if (base == 0x55aa55aa || base == 0x0) {
- dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n");
- goto irq;
- }
-
- dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_IMR_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_IMR_BAR]) {
- dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
- base, size);
- return -ENODEV;
- }
- dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]);
-
-irq:
- /* register our IRQ */
- sdev->ipc_irq = pci->irq;
- dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
- ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
- byt_irq_handler, byt_irq_thread,
- 0, "AudioDSP", sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to register IRQ %d\n",
- sdev->ipc_irq);
- return ret;
- }
-
- /* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
- SHIM_IMRX_DONE);
-
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = MBOX_OFFSET;
-
- return ret;
-}
-
-const struct snd_sof_dsp_ops sof_tng_ops = {
- /* device init */
- .probe = tangier_pci_probe,
-
- /* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
-
- /* Register IO */
- .write = sof_io_write,
- .read = sof_io_read,
- .write64 = sof_io_write64,
- .read64 = sof_io_read64,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
-
- /* ipc */
- .send_msg = byt_send_msg,
- .fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
-
- .ipc_msg_data = intel_ipc_msg_data,
- .ipc_pcm_params = intel_ipc_pcm_params,
-
- /* machine driver */
- .machine_select = byt_machine_select,
- .machine_register = sof_machine_register,
- .machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
-
- /* debug */
- .debug_map = byt_debugfs,
- .debug_map_count = ARRAY_SIZE(byt_debugfs),
- .dbg_dump = byt_dump,
-
- /* stream callbacks */
- .pcm_open = intel_pcm_open,
- .pcm_close = intel_pcm_close,
-
- /* module loading */
- .load_module = snd_sof_parse_module_memcpy,
-
- /*Firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
-
- /* DAI drivers */
- .drv = byt_dai,
- .num_drv = 3, /* we have only 3 SSPs on byt*/
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH,
-
- .arch_ops = &sof_xtensa_arch_ops,
-};
-EXPORT_SYMBOL_NS(sof_tng_ops, SND_SOC_SOF_MERRIFIELD);
-
-const struct sof_intel_dsp_desc tng_chip_info = {
- .cores_num = 1,
- .host_managed_cores_mask = 1,
+static const struct snd_sof_debugfs_map cht_debugfs[] = {
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac2", DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp3", DSP_BAR, SSP3_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp4", DSP_BAR, SSP4_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp5", DSP_BAR, SSP5_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
};
-EXPORT_SYMBOL_NS(tng_chip_info, SND_SOC_SOF_MERRIFIELD);
-
-#endif /* CONFIG_SND_SOC_SOF_MERRIFIELD */
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
static void byt_reset_dsp_disable_int(struct snd_sof_dev *sdev)
{
/* Disable Interrupt from both sides */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x3);
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x3);
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX, 0x3, 0x3);
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRD, 0x3, 0x3);
/* Put DSP into reset, set reset vector */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL,
SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL);
}
@@ -689,7 +93,7 @@ static int byt_suspend(struct snd_sof_dev *sdev, u32 target_state)
static int byt_resume(struct snd_sof_dev *sdev)
{
/* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
@@ -703,33 +107,6 @@ static int byt_remove(struct snd_sof_dev *sdev)
return 0;
}
-static const struct snd_sof_debugfs_map cht_debugfs[] = {
- {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
- SOF_DEBUGFS_ACCESS_ALWAYS},
-};
-
static int byt_acpi_probe(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
@@ -760,17 +137,17 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
}
dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_DSP_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_DSP_BAR]) {
+ sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[DSP_BAR]) {
dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
base, size);
return -ENODEV;
}
- dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[BYT_DSP_BAR]);
+ dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[DSP_BAR]);
/* TODO: add offsets */
- sdev->mmio_bar = BYT_DSP_BAR;
- sdev->mailbox_bar = BYT_DSP_BAR;
+ sdev->mmio_bar = DSP_BAR;
+ sdev->mailbox_bar = DSP_BAR;
/* IMR base - optional */
if (desc->resindex_imr_base == -1)
@@ -794,13 +171,13 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
}
dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
- sdev->bar[BYT_IMR_BAR] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[BYT_IMR_BAR]) {
+ sdev->bar[IMR_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[IMR_BAR]) {
dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
base, size);
return -ENODEV;
}
- dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[BYT_IMR_BAR]);
+ dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[IMR_BAR]);
irq:
/* register our IRQ */
@@ -810,7 +187,7 @@ irq:
dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
- byt_irq_handler, byt_irq_thread,
+ atom_irq_handler, atom_irq_thread,
IRQF_SHARED, "AudioDSP", sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to register IRQ %d\n",
@@ -819,7 +196,7 @@ irq:
}
/* enable BUSY and disable DONE Interrupt by default */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
SHIM_IMRX_DONE);
@@ -836,8 +213,8 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.remove = byt_remove,
/* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
+ .run = atom_run,
+ .reset = atom_reset,
/* Register IO */
.write = sof_io_write,
@@ -850,28 +227,28 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.block_write = sof_block_write,
/* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
/* ipc */
- .send_msg = byt_send_msg,
+ .send_msg = atom_send_msg,
.fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
/* machine driver */
- .machine_select = byt_machine_select,
+ .machine_select = atom_machine_select,
.machine_register = sof_machine_register,
.machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
+ .set_mach_params = atom_set_mach_params,
/* debug */
.debug_map = byt_debugfs,
.debug_map_count = ARRAY_SIZE(byt_debugfs),
- .dbg_dump = byt_dump,
+ .dbg_dump = atom_dump,
/* stream callbacks */
.pcm_open = intel_pcm_open,
@@ -888,7 +265,7 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.resume = byt_resume,
/* DAI drivers */
- .drv = byt_dai,
+ .drv = atom_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
/* ALSA HW info flags */
@@ -913,8 +290,8 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.remove = byt_remove,
/* DSP core boot / reset */
- .run = byt_run,
- .reset = byt_reset,
+ .run = atom_run,
+ .reset = atom_reset,
/* Register IO */
.write = sof_io_write,
@@ -927,28 +304,28 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.block_write = sof_block_write,
/* doorbell */
- .irq_handler = byt_irq_handler,
- .irq_thread = byt_irq_thread,
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
/* ipc */
- .send_msg = byt_send_msg,
+ .send_msg = atom_send_msg,
.fw_ready = sof_fw_ready,
- .get_mailbox_offset = byt_get_mailbox_offset,
- .get_window_offset = byt_get_window_offset,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
/* machine driver */
- .machine_select = byt_machine_select,
+ .machine_select = atom_machine_select,
.machine_register = sof_machine_register,
.machine_unregister = sof_machine_unregister,
- .set_mach_params = byt_set_mach_params,
+ .set_mach_params = atom_set_mach_params,
/* debug */
.debug_map = cht_debugfs,
.debug_map_count = ARRAY_SIZE(cht_debugfs),
- .dbg_dump = byt_dump,
+ .dbg_dump = atom_dump,
/* stream callbacks */
.pcm_open = intel_pcm_open,
@@ -965,9 +342,9 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.resume = byt_resume,
/* DAI drivers */
- .drv = byt_dai,
+ .drv = atom_dai,
/* all 6 SSPs may be available for cherrytrail */
- .num_drv = ARRAY_SIZE(byt_dai),
+ .num_drv = 6,
/* ALSA HW info flags */
.hw_info = SNDRV_PCM_INFO_MMAP |
@@ -1073,9 +450,8 @@ static struct platform_driver snd_sof_acpi_intel_byt_driver = {
};
module_platform_driver(snd_sof_acpi_intel_byt_driver);
-#endif /* CONFIG_SND_SOC_SOF_BAYTRAIL */
-
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC);
MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_IMPORT_NS(SND_SOC_SOF_ACPI_DEV);
+MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 8d7bab433fb3..c1f9f0f58464 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -421,11 +421,16 @@ static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;
+ /* DAI_CONFIG IPC during hw_params is not supported in older firmware */
+ if (v->abi_version < SOF_ABI_VER(3, 18, 0))
+ return 0;
+
list_for_each_entry(sof_dai, &sdev->dai_list, list) {
if (!sof_dai->cpu_dai_name || !sof_dai->dai_config)
continue;
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index fc25ee8f68dc..6f4771bf9de3 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -385,11 +385,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
if (i == HDA_FW_BOOT_ATTEMPTS) {
dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n",
i, ret);
- dev_err(sdev->dev, "ROM error=0x%x: FW status=0x%x\n",
- snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_ERROR),
- snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_ROM_STATUS));
goto cleanup;
}
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index b00e8fcb2312..e1e368ff2b12 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -277,10 +277,12 @@ struct hda_dsp_msg_code {
const char *msg;
};
-static bool hda_use_msi = IS_ENABLED(CONFIG_PCI);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG)
+static bool hda_use_msi = true;
module_param_named(use_msi, hda_use_msi, bool, 0444);
MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
+#else
+#define hda_use_msi (1)
#endif
static char *hda_model;
@@ -392,28 +394,21 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[HDA_DSP_STACK_DUMP_SIZE];
- u32 status, panic;
- /* try APL specific status message types first */
+ /* print ROM/FW status */
hda_dsp_get_status(sdev);
- /* now try generic SOF status messages */
- status = snd_sof_dsp_read(sdev, HDA_DSP_BAR,
- HDA_DSP_SRAM_REG_FW_STATUS);
- panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
-
+ /* print panic info if FW boot is complete. Otherwise, print the extended ROM status */
if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
+ u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS);
+ u32 panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP);
+
hda_dsp_get_registers(sdev, &xoops, &panic_info, stack,
HDA_DSP_STACK_DUMP_SIZE);
snd_sof_get_status(sdev, status, panic, &xoops, &panic_info,
stack, HDA_DSP_STACK_DUMP_SIZE);
} else {
- sof_dev_dbg_or_err(sdev->dev, flags & SOF_DBG_DUMP_FORCE_ERR_LEVEL,
- "status = 0x%8.8x panic = 0x%8.8x\n",
- status, panic);
-
hda_dsp_dump_ext_rom_status(sdev, flags);
- hda_dsp_get_status(sdev);
}
}
@@ -485,9 +480,7 @@ static int hda_init(struct snd_sof_dev *sdev)
/* initialise hdac bus */
bus->addr = pci_resource_start(pci, 0);
-#if IS_ENABLED(CONFIG_PCI)
bus->remap_addr = pci_ioremap_bar(pci, 0);
-#endif
if (!bus->remap_addr) {
dev_err(bus->dev, "error: ioremap error\n");
return -ENXIO;
@@ -799,9 +792,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
goto hdac_bus_unmap;
/* DSP base */
-#if IS_ENABLED(CONFIG_PCI)
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
-#endif
if (!sdev->bar[HDA_DSP_BAR]) {
dev_err(sdev->dev, "error: ioremap error\n");
ret = -ENXIO;
@@ -1069,7 +1060,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev,
/* find out how many identical parts are expected */
for (k = 0; k < link->num_adr; k++) {
- u64 adr2 = link->adr_d[i].adr;
+ u64 adr2 = link->adr_d[k].adr;
unsigned int part_id2, link_id2, mfg_id2;
mfg_id2 = SDW_MFG_ID(adr2);
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index 88c3bf404dd7..a00262184efa 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -116,6 +116,8 @@ static const struct pci_device_id sof_pci_ids[] = {
.driver_data = (unsigned long)&adls_desc},
{ PCI_DEVICE(0x8086, 0x51c8), /* ADL-P */
.driver_data = (unsigned long)&adl_desc},
+ { PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */
+ .driver_data = (unsigned long)&adl_desc},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
index 94b9704c0117..4ee1da397d4e 100644
--- a/sound/soc/sof/intel/pci-tng.c
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -14,7 +14,10 @@
#include <sound/soc-acpi-intel-match.h>
#include <sound/sof.h>
#include "../ops.h"
+#include "atom.h"
+#include "shim.h"
#include "../sof-pci-dev.h"
+#include "../sof-audio.h"
/* platform specific devices */
#include "shim.h"
@@ -29,6 +32,170 @@ static struct snd_soc_acpi_mach sof_tng_machines[] = {
{}
};
+static const struct snd_sof_debugfs_map tng_debugfs[] = {
+ {"dmac0", DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac1", DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp0", DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp1", DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp2", DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"iram", DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"dram", DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"shim", DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+};
+
+static int tangier_pci_probe(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *pdata = sdev->pdata;
+ const struct sof_dev_desc *desc = pdata->desc;
+ struct pci_dev *pci = to_pci_dev(sdev->dev);
+ u32 base, size;
+ int ret;
+
+ /* DSP DMA can only access low 31 bits of host memory */
+ ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to set DMA mask %d\n", ret);
+ return ret;
+ }
+
+ /* LPE base */
+ base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET;
+ size = PCI_BAR_SIZE;
+
+ dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size);
+ sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[DSP_BAR]) {
+ dev_err(sdev->dev, "error: failed to ioremap LPE base 0x%x size 0x%x\n",
+ base, size);
+ return -ENODEV;
+ }
+ dev_dbg(sdev->dev, "LPE VADDR %p\n", sdev->bar[DSP_BAR]);
+
+ /* IMR base - optional */
+ if (desc->resindex_imr_base == -1)
+ goto irq;
+
+ base = pci_resource_start(pci, desc->resindex_imr_base);
+ size = pci_resource_len(pci, desc->resindex_imr_base);
+
+ /* some BIOSes don't map IMR */
+ if (base == 0x55aa55aa || base == 0x0) {
+ dev_info(sdev->dev, "IMR not set by BIOS. Ignoring\n");
+ goto irq;
+ }
+
+ dev_dbg(sdev->dev, "IMR base at 0x%x size 0x%x", base, size);
+ sdev->bar[IMR_BAR] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[IMR_BAR]) {
+ dev_err(sdev->dev, "error: failed to ioremap IMR base 0x%x size 0x%x\n",
+ base, size);
+ return -ENODEV;
+ }
+ dev_dbg(sdev->dev, "IMR VADDR %p\n", sdev->bar[IMR_BAR]);
+
+irq:
+ /* register our IRQ */
+ sdev->ipc_irq = pci->irq;
+ dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
+ ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
+ atom_irq_handler, atom_irq_thread,
+ 0, "AudioDSP", sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to register IRQ %d\n",
+ sdev->ipc_irq);
+ return ret;
+ }
+
+ /* enable BUSY and disable DONE Interrupt by default */
+ snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
+
+ return ret;
+}
+
+const struct snd_sof_dsp_ops sof_tng_ops = {
+ /* device init */
+ .probe = tangier_pci_probe,
+
+ /* DSP core boot / reset */
+ .run = atom_run,
+ .reset = atom_reset,
+
+ /* Register IO */
+ .write = sof_io_write,
+ .read = sof_io_read,
+ .write64 = sof_io_write64,
+ .read64 = sof_io_read64,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* doorbell */
+ .irq_handler = atom_irq_handler,
+ .irq_thread = atom_irq_thread,
+
+ /* ipc */
+ .send_msg = atom_send_msg,
+ .fw_ready = sof_fw_ready,
+ .get_mailbox_offset = atom_get_mailbox_offset,
+ .get_window_offset = atom_get_window_offset,
+
+ .ipc_msg_data = intel_ipc_msg_data,
+ .ipc_pcm_params = intel_ipc_pcm_params,
+
+ /* machine driver */
+ .machine_select = atom_machine_select,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+ .set_mach_params = atom_set_mach_params,
+
+ /* debug */
+ .debug_map = tng_debugfs,
+ .debug_map_count = ARRAY_SIZE(tng_debugfs),
+ .dbg_dump = atom_dump,
+
+ /* stream callbacks */
+ .pcm_open = intel_pcm_open,
+ .pcm_close = intel_pcm_close,
+
+ /* module loading */
+ .load_module = snd_sof_parse_module_memcpy,
+
+ /*Firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* DAI drivers */
+ .drv = atom_dai,
+ .num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH,
+
+ .arch_ops = &sof_xtensa_arch_ops,
+};
+
+const struct sof_intel_dsp_desc tng_chip_info = {
+ .cores_num = 1,
+ .host_managed_cores_mask = 1,
+};
+
static const struct sof_dev_desc tng_desc = {
.machines = sof_tng_machines,
.resindex_lpe_base = 3, /* IRAM, but subtract IRAM offset */
@@ -66,5 +233,7 @@ static struct pci_driver snd_sof_pci_intel_tng_driver = {
module_pci_driver(snd_sof_pci_intel_tng_driver);
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_IMPORT_NS(SND_SOC_SOF_MERRIFIELD);
+MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HIFI_EP_IPC);
+MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
+MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 6efaf766f2ab..2b38a77cd594 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -517,7 +517,7 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
return 0;
/* copy data from the DSP FW ready offset */
- sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
+ snd_sof_dsp_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
/* make sure ABI version is compatible */
ret = snd_sof_ipc_valid(sdev);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 323a0b3f561b..4a5d6e497f05 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -244,13 +244,13 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev,
static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags)
{
if (sof_ops(sdev)->dbg_dump)
- return sof_ops(sdev)->dbg_dump(sdev, flags);
+ sof_ops(sdev)->dbg_dump(sdev, flags);
}
static inline void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
{
if (sof_ops(sdev)->ipc_dump)
- return sof_ops(sdev)->ipc_dump(sdev);
+ sof_ops(sdev)->ipc_dump(sdev);
}
/* register IO */
@@ -546,14 +546,16 @@ static inline const struct snd_sof_dsp_ops
(val) = snd_sof_dsp_read(sdev, bar, offset); \
if (cond) { \
dev_dbg(sdev->dev, \
- "FW Poll Status: reg=%#x successful\n", (val)); \
+ "FW Poll Status: reg[%#x]=%#x successful\n", \
+ (offset), (val)); \
break; \
} \
if (__timeout_us && \
ktime_compare(ktime_get(), __timeout) > 0) { \
(val) = snd_sof_dsp_read(sdev, bar, offset); \
dev_dbg(sdev->dev, \
- "FW Poll Status: reg=%#x timedout\n", (val)); \
+ "FW Poll Status: reg[%#x]=%#x timedout\n", \
+ (offset), (val)); \
break; \
} \
if (__sleep_us) \
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index 7fbf09f9f17e..74982c04497b 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -60,7 +60,6 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
{
struct device *dev = &pdev->dev;
struct snd_sof_pdata *sof_pdata;
- const struct snd_sof_dsp_ops *ops;
dev_dbg(dev, "ACPI DSP detected");
@@ -68,9 +67,7 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
if (!sof_pdata)
return -ENOMEM;
- /* get ops for platform */
- ops = desc->ops;
- if (!ops) {
+ if (!desc->ops) {
dev_err(dev, "error: no matching ACPI descriptor ops\n");
return -ENODEV;
}
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index c9c70645b377..d1a21edfa05d 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -70,7 +70,6 @@ static int sof_of_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct sof_dev_desc *desc;
struct snd_sof_pdata *sof_pdata;
- const struct snd_sof_dsp_ops *ops;
dev_info(&pdev->dev, "DT DSP detected");
@@ -82,9 +81,7 @@ static int sof_of_probe(struct platform_device *pdev)
if (!desc)
return -ENODEV;
- /* get ops for platform */
- ops = desc->ops;
- if (!ops) {
+ if (!desc->ops) {
dev_err(dev, "error: no matching DT descriptor ops\n");
return -ENODEV;
}
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index 3489dc1b48f6..03119462f9e2 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -116,14 +116,11 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
const struct sof_dev_desc *desc =
(const struct sof_dev_desc *)pci_id->driver_data;
struct snd_sof_pdata *sof_pdata;
- const struct snd_sof_dsp_ops *ops;
int ret;
dev_dbg(&pci->dev, "PCI DSP detected");
- /* get ops for platform */
- ops = desc->ops;
- if (!ops) {
+ if (!desc->ops) {
dev_err(dev, "error: no matching PCI descriptor ops\n");
return -ENODEV;
}
@@ -141,7 +138,7 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return ret;
sof_pdata->name = pci_name(pci);
- sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data;
+ sof_pdata->desc = desc;
sof_pdata->dev = dev;
sof_pdata->fw_filename = desc->default_fw_filename;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 59abcfc9bd55..cc9585bfa4e9 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1063,6 +1063,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
scontrol->min_volume_step = le32_to_cpu(mc->min);
scontrol->max_volume_step = le32_to_cpu(mc->max);
scontrol->num_channels = le32_to_cpu(mc->num_channels);
+ scontrol->control_data->index = kc->index;
/* set cmd for mixer control */
if (le32_to_cpu(mc->max) == 1) {
@@ -1140,7 +1141,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp,
scontrol->comp_id = sdev->next_comp_id;
scontrol->num_channels = le32_to_cpu(ec->num_channels);
-
+ scontrol->control_data->index = kc->index;
scontrol->cmd = SOF_CTRL_CMD_ENUM;
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
@@ -1188,6 +1189,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
scontrol->comp_id = sdev->next_comp_id;
scontrol->cmd = SOF_CTRL_CMD_BINARY;
+ scontrol->control_data->index = kc->index;
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
scontrol->comp_id, scontrol->num_channels);
@@ -2133,7 +2135,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
for (i = 0; i < widget->num_kcontrols; i++) {
kc = &widget->kcontrol_news[i];
- switch (widget->dobj.widget.kcontrol_type) {
+ switch (widget->dobj.widget.kcontrol_type[i]) {
case SND_SOC_TPLG_TYPE_MIXER:
sm = (struct soc_mixer_control *)kc->private_value;
wdata[i].control = sm->dobj.private;
@@ -2147,8 +2149,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
wdata[i].control = se->dobj.private;
break;
default:
- dev_err(scomp->dev, "error: unknown kcontrol type %d in widget %s\n",
- widget->dobj.widget.kcontrol_type,
+ dev_err(scomp->dev, "error: unknown kcontrol type %u in widget %s\n",
+ widget->dobj.widget.kcontrol_type[i],
widget->name);
return -EINVAL;
}
@@ -2164,7 +2166,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
return -EINVAL;
/* make sure data is valid - data can be updated at runtime */
- if (wdata[i].pdata->magic != SOF_ABI_MAGIC)
+ if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES &&
+ wdata[i].pdata->magic != SOF_ABI_MAGIC)
return -EINVAL;
*size += wdata[i].pdata->size;
@@ -2605,7 +2608,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
}
for (i = 0; i < widget->num_kcontrols; i++) {
kc = &widget->kcontrol_news[i];
- switch (dobj->widget.kcontrol_type) {
+ switch (widget->dobj.widget.kcontrol_type[i]) {
case SND_SOC_TPLG_TYPE_MIXER:
sm = (struct soc_mixer_control *)kc->private_value;
scontrol = sm->dobj.private;
@@ -3335,7 +3338,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
/* Copy common data to all config ipc structs */
for (i = 0; i < num_conf; i++) {
config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config[i].format = hw_config[i].fmt;
+ config[i].format = le32_to_cpu(hw_config[i].fmt);
config[i].type = common_config.type;
config[i].dai_index = common_config.dai_index;
}
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index c1561237ee24..3aa1cf262402 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -484,10 +484,7 @@ static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai)
dev_err(dev, "mclk register returned %d\n", ret);
return ret;
}
-
- sai->sai_mclk = devm_clk_hw_get_clk(dev, hw, NULL);
- if (IS_ERR(sai->sai_mclk))
- return PTR_ERR(sai->sai_mclk);
+ sai->sai_mclk = hw->clk;
/* register mclk provider */
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index b94220306d1a..017a5a5e56cd 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -2317,6 +2317,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
break;
default:
dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
+ fallthrough;
case -EPROBE_DEFER:
goto err;
}
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index db47981768c5..4479d74f0a45 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -539,7 +539,7 @@ static ssize_t prop##_store(struct device *dev, \
return size; \
} \
\
-static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store)
+static DEVICE_ATTR_RW(prop)
THRESHOLD_PROP_BUILDER(max_tx_thres);
THRESHOLD_PROP_BUILDER(max_rx_thres);