diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 21:29:05 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 21:29:05 +0300 |
commit | 064d7dcf51a82b480e953a15cca47e5df0426502 (patch) | |
tree | b34ab58ee35e827c14071024dfc79fe50a5f31e6 | |
parent | f3dd0c53370e70c0f9b7e931bbec12916f3bb8cc (diff) | |
parent | 7933b90b42896f5b6596e6a829bb31c5121fc2a9 (diff) | |
download | linux-064d7dcf51a82b480e953a15cca47e5df0426502.tar.xz |
Merge tag 'sound-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"The majority of works in this cycle are about ASoC spread over trees.
Most of them are for new devices and cleanups / refactoring works, and
not much significant changes are seen in the core side.
Below are some highlights:
ASoC:
- Continued refactoring to move into common helper functions
- Lots of DT schema conversons and stylistic nits
- Continued work on building out the new SOF IPC4 scheme
- Continued work for Intel AVS
- New drivers for Awinc AT88395, Infineon PEB2466, Iron Device
SMA1303, Mediatek MT8188, Realtek RT712, Renesas IDT821034,
Samsung/Tesla FSD SoC I2S, and TI TAS5720A-Q1
ALSA:
- A few cleanups to make the remove callbacks to void returns
- FireWire refactoring and enhancements
- PCM kselftest enhancements"
* tag 'sound-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (398 commits)
ALSA: hda/hdmi: Register with vga_switcheroo on Dual GPU Macbooks
ASoC: soc-ac97: Return correct error codes
ASoC: soc-dapm.h: fixup warning struct snd_pcm_substream not declared
ASoC: cs35l45: Remove separate namespace for tables
ASoC: cs35l45: Remove separate tables module
ASoC: soc-ac97: Convert to agnostic GPIO API
ASoC: dt-bindings: renesas,rsnd.yaml: drop "dmas/dma-names" from "rcar_sound,ssi"
ALSA: hda: cs35l41: Enable Amp High Pass Filter
ALSA: hda: cs35l41: Ensure firmware/tuning pairs are always loaded
ALSA: hda: cs35l41: Correct error condition handling
ASoC: codecs: wcd934x: Use min macro for comparison and assignment
ASoC: Intel: Skylake: Fix struct definition
ASoC: tlv320adcx140: extend list of supported samplerates
ASoC: imx-pcm-rpmsg: Remove unused variable
SoC: rt5682s: Disable jack detection interrupt during suspend
ASoC: SOF: Intel: hda-dsp: Set streaming flag for d0i3
ASoC: SOF: Intel: Enable d0i3 work for ipc4
ASoC: SOF: ipc4: Wake up dsp core before sending ipc msg
ASoC: SOF: Intel: hda-dsp: use set_pm_gate according to ipc version
ASoC: SOF: Introduce a new set_pm_gate() IPC PM op
...
379 files changed, 33352 insertions, 3928 deletions
diff --git a/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml b/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml index 3e63f79890b4..88575da1e6d5 100644 --- a/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml +++ b/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml @@ -15,7 +15,9 @@ description: | properties: compatible: - const: mediatek,mt8186-dsp + enum: + - mediatek,mt8186-dsp + - mediatek,mt8188-dsp reg: items: diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.txt b/Documentation/devicetree/bindings/sound/adi,adau7002.txt deleted file mode 100644 index f144ee1abf85..000000000000 --- a/Documentation/devicetree/bindings/sound/adi,adau7002.txt +++ /dev/null @@ -1,19 +0,0 @@ -Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter - -Required properties: - - - compatible: Must be "adi,adau7002" - -Optional properties: - - - IOVDD-supply: Phandle and specifier for the power supply providing the IOVDD - supply as covered in Documentation/devicetree/bindings/regulator/regulator.txt - - If this property is not present it is assumed that the supply pin is - hardwired to always on. - -Example: - adau7002: pdm-to-i2s { - compatible = "adi,adau7002"; - IOVDD-supply = <&supply>; - }; diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.yaml b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml new file mode 100644 index 000000000000..fcca0fde7d86 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/adi,adau7002.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter + +maintainers: + - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: adi,adau7002 + + IOVDD-supply: + description: + IOVDD power supply, if skipped then it is assumed that the supply pin is + hardwired to always on. + + wakeup-delay-ms: + description: + Delay after power up needed for device to settle. + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + audio-codec { + compatible = "adi,adau7002"; + IOVDD-supply = <&pp1800_l15a>; + #sound-dai-cells = <0>; + wakeup-delay-ms = <80>; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4613.yaml b/Documentation/devicetree/bindings/sound/ak4613.yaml index 010574645e6a..75e13414d6eb 100644 --- a/Documentation/devicetree/bindings/sound/ak4613.yaml +++ b/Documentation/devicetree/bindings/sound/ak4613.yaml @@ -25,6 +25,13 @@ properties: "#sound-dai-cells": const: 0 + ports: + $ref: audio-graph-port.yaml#/definitions/ports + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + patternProperties: "^asahi-kasei,in[1-2]-single-end$": description: Input Pin 1 - 2. diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt deleted file mode 100644 index fa4545ed81ca..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt +++ /dev/null @@ -1,34 +0,0 @@ -* Amlogic Audio FIFO controllers - -Required properties: -- compatible: 'amlogic,axg-toddr' or - 'amlogic,axg-toddr' or - 'amlogic,g12a-frddr' or - 'amlogic,g12a-toddr' or - 'amlogic,sm1-frddr' or - 'amlogic,sm1-toddr' -- reg: physical base address of the controller and length of memory - mapped region. -- interrupts: interrupt specifier for the fifo. -- clocks: phandle to the fifo peripheral clock provided by the audio - clock controller. -- resets: list of reset phandle, one for each entry reset-names. -- reset-names: should contain the following: - * "arb" : memory ARB line (required) - * "rst" : dedicated device reset line (optional) -- #sound-dai-cells: must be 0. -- amlogic,fifo-depth: The size of the controller's fifo in bytes. This - is useful for determining certain configuration such - as the flush threshold of the fifo - -Example of FRDDR A on the A113 SoC: - -frddr_a: audio-controller@1c0 { - compatible = "amlogic,axg-frddr"; - reg = <0x0 0x1c0 0x0 0x1c>; - #sound-dai-cells = <0>; - interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>; - clocks = <&clkc_audio AUD_CLKID_FRDDR_A>; - resets = <&arb AXG_ARB_FRDDR_A>; - fifo-depth = <512>; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml new file mode 100644 index 000000000000..b1b48d683101 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-fifo.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic AXG Audio FIFO controllers + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + oneOf: + - enum: + - amlogic,axg-toddr + - amlogic,axg-frddr + - items: + - enum: + - amlogic,g12a-toddr + - amlogic,sm1-toddr + - const: amlogic,axg-toddr + - items: + - enum: + - amlogic,g12a-frddr + - amlogic,sm1-frddr + - const: amlogic,axg-frddr + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + items: + - description: Peripheral clock + + interrupts: + maxItems: 1 + + resets: + minItems: 1 + maxItems: 2 + + reset-names: + minItems: 1 + maxItems: 2 + + amlogic,fifo-depth: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Size of the controller's fifo in bytes + +required: + - compatible + - reg + - "#sound-dai-cells" + - clocks + - interrupts + - resets + - amlogic,fifo-depth + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - amlogic,g12a-toddr + - amlogic,sm1-toddr + - amlogic,g12a-frddr + - amlogic,sm1-frddr + + then: + properties: + resets: + minItems: 2 + reset-names: + items: + - const: arb + - const: rst + required: + - reset-names + + else: + properties: + resets: + maxItems: 1 + reset-names: + const: arb + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h> + #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h> + + audio-controller@1c0 { + compatible = "amlogic,g12a-frddr", "amlogic,axg-frddr"; + reg = <0x1c0 0x1c>; + #sound-dai-cells = <0>; + clocks = <&clkc_audio AUD_CLKID_FRDDR_A>; + interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>; + resets = <&arb>, <&clkc_audio AUD_RESET_FRDDR_A>; + reset-names = "arb", "rst"; + amlogic,fifo-depth = <512>; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt deleted file mode 100644 index 716878107a24..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt +++ /dev/null @@ -1,29 +0,0 @@ -* Amlogic Audio PDM input - -Required properties: -- compatible: 'amlogic,axg-pdm' or - 'amlogic,g12a-pdm' or - 'amlogic,sm1-pdm' -- reg: physical base address of the controller and length of memory - mapped region. -- clocks: list of clock phandle, one for each entry clock-names. -- clock-names: should contain the following: - * "pclk" : peripheral clock. - * "dclk" : pdm digital clock - * "sysclk" : dsp system clock -- #sound-dai-cells: must be 0. - -Optional property: -- resets: phandle to the dedicated reset line of the pdm input. - -Example of PDM on the A113 SoC: - -pdm: audio-controller@ff632000 { - compatible = "amlogic,axg-pdm"; - reg = <0x0 0xff632000 0x0 0x34>; - #sound-dai-cells = <0>; - clocks = <&clkc_audio AUD_CLKID_PDM>, - <&clkc_audio AUD_CLKID_PDM_DCLK>, - <&clkc_audio AUD_CLKID_PDM_SYSCLK>; - clock-names = "pclk", "dclk", "sysclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml new file mode 100644 index 000000000000..df21dd72fc65 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-pdm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Audio AXG PDM input + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + oneOf: + - items: + - enum: + - amlogic,g12a-pdm + - amlogic,sm1-pdm + - const: amlogic,axg-pdm + - const: amlogic,axg-pdm + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + items: + - description: Peripheral clock + - description: PDM digital clock + - description: DSP system clock + + clock-names: + items: + - const: pclk + - const: dclk + - const: sysclk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - "#sound-dai-cells" + - clocks + - clock-names + +allOf: + - $ref: dai-common.yaml# + + - if: + properties: + compatible: + contains: + enum: + - amlogic,g12a-pdm + - amlogic,sm1-pdm + then: + required: + - resets + + else: + properties: + resets: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + + audio-controller@ff632000 { + compatible = "amlogic,axg-pdm"; + reg = <0xff632000 0x34>; + #sound-dai-cells = <0>; + clocks = <&clkc_audio AUD_CLKID_PDM>, + <&clkc_audio AUD_CLKID_PDM_DCLK>, + <&clkc_audio AUD_CLKID_PDM_SYSCLK>; + clock-names = "pclk", "dclk", "sysclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt deleted file mode 100644 index 80b411296480..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt +++ /dev/null @@ -1,124 +0,0 @@ -Amlogic AXG sound card: - -Required properties: - -- compatible: "amlogic,axg-sound-card" -- model : User specified audio sound card name, one string - -Optional properties: - -- audio-aux-devs : List of phandles pointing to auxiliary devices -- audio-widgets : Please refer to widgets.txt. -- audio-routing : A list of the connections between audio components. - -Subnodes: - -- dai-link: Container for dai-link level properties and the CODEC - sub-nodes. There should be at least one (and probably more) - subnode of this type. - -Required dai-link properties: - -- sound-dai: phandle and port of the CPU DAI. - -Required TDM Backend dai-link properties: -- dai-format : CPU/CODEC common audio format - -Optional TDM Backend dai-link properties: -- dai-tdm-slot-rx-mask-{0,1,2,3}: Receive direction slot masks -- dai-tdm-slot-tx-mask-{0,1,2,3}: Transmit direction slot masks - When omitted, mask is assumed to have to no - slots. A valid must have at one slot, so at - least one these mask should be provided with - an enabled slot. -- dai-tdm-slot-num : Please refer to tdm-slot.txt. - If omitted, slot number is set to accommodate the largest - mask provided. -- dai-tdm-slot-width : Please refer to tdm-slot.txt. default to 32 if omitted. -- mclk-fs : Multiplication factor between stream rate and mclk - -Backend dai-link subnodes: - -- codec: dai-link representing backend links should have at least one subnode. - One subnode for each codec of the dai-link. - dai-link representing frontend links have no codec, therefore have no - subnodes - -Required codec subnodes properties: - -- sound-dai: phandle and port of the CODEC DAI. - -Optional codec subnodes properties: - -- dai-tdm-slot-tx-mask : Please refer to tdm-slot.txt. -- dai-tdm-slot-rx-mask : Please refer to tdm-slot.txt. - -Example: - -sound { - compatible = "amlogic,axg-sound-card"; - model = "AXG-S420"; - audio-aux-devs = <&tdmin_a>, <&tdmout_c>; - audio-widgets = "Line", "Lineout", - "Line", "Linein", - "Speaker", "Speaker1 Left", - "Speaker", "Speaker1 Right"; - "Speaker", "Speaker2 Left", - "Speaker", "Speaker2 Right"; - audio-routing = "TDMOUT_C IN 0", "FRDDR_A OUT 2", - "SPDIFOUT IN 0", "FRDDR_A OUT 3", - "TDM_C Playback", "TDMOUT_C OUT", - "TDMIN_A IN 2", "TDM_C Capture", - "TDMIN_A IN 5", "TDM_C Loopback", - "TODDR_A IN 0", "TDMIN_A OUT", - "Lineout", "Lineout AOUTL", - "Lineout", "Lineout AOUTR", - "Speaker1 Left", "SPK1 OUT_A", - "Speaker2 Left", "SPK2 OUT_A", - "Speaker1 Right", "SPK1 OUT_B", - "Speaker2 Right", "SPK2 OUT_B", - "Linein AINL", "Linein", - "Linein AINR", "Linein"; - - dai-link@0 { - sound-dai = <&frddr_a>; - }; - - dai-link@1 { - sound-dai = <&toddr_a>; - }; - - dai-link@2 { - sound-dai = <&tdmif_c>; - dai-format = "i2s"; - dai-tdm-slot-tx-mask-2 = <1 1>; - dai-tdm-slot-tx-mask-3 = <1 1>; - dai-tdm-slot-rx-mask-1 = <1 1>; - mclk-fs = <256>; - - codec@0 { - sound-dai = <&lineout>; - }; - - codec@1 { - sound-dai = <&speaker_amp1>; - }; - - codec@2 { - sound-dai = <&speaker_amp2>; - }; - - codec@3 { - sound-dai = <&linein>; - }; - - }; - - dai-link@3 { - sound-dai = <&spdifout>; - - codec { - sound-dai = <&spdif_dit>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml new file mode 100644 index 000000000000..bf1234550343 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml @@ -0,0 +1,183 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-sound-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic AXG sound card + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + const: amlogic,axg-sound-card + + audio-aux-devs: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: list of auxiliary devices + + 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. + + audio-widgets: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: + A list off component DAPM widget. Each entry is a pair of strings, + the first being the widget type, the second being the widget name + + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + +patternProperties: + "^dai-link-[0-9]+$": + type: object + additionalProperties: false + description: + Container for dai-link level properties and the CODEC sub-nodes. + There should be at least one (and probably more) subnode of this type + + properties: + dai-format: + $ref: /schemas/types.yaml#/definitions/string + enum: [ i2s, left-j, dsp_a ] + + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Number of slots in use. If omitted, slot number is set to + accommodate the largest mask provided. + maximum: 32 + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width in bits for each slot + enum: [ 8, 16, 20, 24, 32 ] + default: 32 + + mclk-fs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Multiplication factor between the frame rate and master clock + rate + + sound-dai: + maxItems: 1 + description: phandle of the CPU DAI + + patternProperties: + "^dai-tdm-slot-(t|r)x-mask-[0-3]$": + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 32 + description: + Transmit and receive cpu slot masks of each TDM lane + When omitted, mask is assumed to have to no slots. A valid + interface must have at least one slot, so at least one these + mask should be provided with an enabled slot. + + "^codec(-[0-9]+)?$": + type: object + additionalProperties: false + description: + dai-link representing backend links should have at least one subnode. + One subnode for each codec of the dai-link. dai-link representing + frontend links have no codec, therefore have no subnodes + + properties: + sound-dai: + maxItems: 1 + description: phandle of the codec DAI + + patternProperties: + "^dai-tdm-slot-(t|r)x-mask$": + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 32 + description: Transmit and receive codec slot masks + + required: + - sound-dai + + required: + - sound-dai + +required: + - model + - dai-link-0 + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "amlogic,axg-sound-card"; + model = "AXG-S420"; + audio-aux-devs = <&tdmin_a>, <&tdmout_c>; + audio-widgets = "Line", "Lineout", + "Line", "Linein", + "Speaker", "Speaker1 Left", + "Speaker", "Speaker1 Right", + "Speaker", "Speaker2 Left", + "Speaker", "Speaker2 Right"; + audio-routing = "TDMOUT_C IN 0", "FRDDR_A OUT 2", + "SPDIFOUT IN 0", "FRDDR_A OUT 3", + "TDM_C Playback", "TDMOUT_C OUT", + "TDMIN_A IN 2", "TDM_C Capture", + "TDMIN_A IN 5", "TDM_C Loopback", + "TODDR_A IN 0", "TDMIN_A OUT", + "Lineout", "Lineout AOUTL", + "Lineout", "Lineout AOUTR", + "Speaker1 Left", "SPK1 OUT_A", + "Speaker2 Left", "SPK2 OUT_A", + "Speaker1 Right", "SPK1 OUT_B", + "Speaker2 Right", "SPK2 OUT_B", + "Linein AINL", "Linein", + "Linein AINR", "Linein"; + + dai-link-0 { + sound-dai = <&frddr_a>; + }; + + dai-link-1 { + sound-dai = <&toddr_a>; + }; + + dai-link-2 { + sound-dai = <&tdmif_c>; + dai-format = "i2s"; + dai-tdm-slot-tx-mask-2 = <1 1>; + dai-tdm-slot-tx-mask-3 = <1 1>; + dai-tdm-slot-rx-mask-1 = <1 1>; + mclk-fs = <256>; + + codec-0 { + sound-dai = <&lineout>; + }; + + codec-1 { + sound-dai = <&speaker_amp1>; + }; + + codec-2 { + sound-dai = <&speaker_amp2>; + }; + + codec-3 { + sound-dai = <&linein>; + }; + }; + + dai-link-3 { + sound-dai = <&spdifout>; + + codec { + sound-dai = <&spdif_dit>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt deleted file mode 100644 index df92a4ecf288..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt +++ /dev/null @@ -1,27 +0,0 @@ -* Amlogic Audio SPDIF Input - -Required properties: -- compatible: 'amlogic,axg-spdifin' or - 'amlogic,g12a-spdifin' or - 'amlogic,sm1-spdifin' -- interrupts: interrupt specifier for the spdif input. -- clocks: list of clock phandle, one for each entry clock-names. -- clock-names: should contain the following: - * "pclk" : peripheral clock. - * "refclk" : spdif input reference clock -- #sound-dai-cells: must be 0. - -Optional property: -- resets: phandle to the dedicated reset line of the spdif input. - -Example on the A113 SoC: - -spdifin: audio-controller@400 { - compatible = "amlogic,axg-spdifin"; - reg = <0x0 0x400 0x0 0x30>; - #sound-dai-cells = <0>; - interrupts = <GIC_SPI 87 IRQ_TYPE_EDGE_RISING>; - clocks = <&clkc_audio AUD_CLKID_SPDIFIN>, - <&clkc_audio AUD_CLKID_SPDIFIN_CLK>; - clock-names = "pclk", "refclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml new file mode 100644 index 000000000000..a0bd7a5fb9b3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-spdifin.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Audio AXG SPDIF Input + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + oneOf: + - const: amlogic,axg-spdifin + - items: + - enum: + - amlogic,g12a-spdifin + - amlogic,sm1-spdifin + - const: amlogic,axg-spdifin + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + items: + - description: Peripheral clock + - description: SPDIF input reference clock + + clock-names: + items: + - const: pclk + - const: refclk + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - "#sound-dai-cells" + - clocks + - clock-names + - interrupts + +allOf: + - $ref: dai-common.yaml# + + - if: + properties: + compatible: + contains: + enum: + - amlogic,g12a-spdifin + - amlogic,sm1-spdifin + then: + required: + - resets + + else: + properties: + resets: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + audio-controller@400 { + compatible = "amlogic,axg-spdifin"; + reg = <0x400 0x30>; + #sound-dai-cells = <0>; + interrupts = <GIC_SPI 87 IRQ_TYPE_EDGE_RISING>; + clocks = <&clkc_audio AUD_CLKID_SPDIFIN>, + <&clkc_audio AUD_CLKID_SPDIFIN_CLK>; + clock-names = "pclk", "refclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt deleted file mode 100644 index 28381dd1f633..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt +++ /dev/null @@ -1,25 +0,0 @@ -* Amlogic Audio SPDIF Output - -Required properties: -- compatible: 'amlogic,axg-spdifout' or - 'amlogic,g12a-spdifout' or - 'amlogic,sm1-spdifout' -- clocks: list of clock phandle, one for each entry clock-names. -- clock-names: should contain the following: - * "pclk" : peripheral clock. - * "mclk" : master clock -- #sound-dai-cells: must be 0. - -Optional property: -- resets: phandle to the dedicated reset line of the spdif output. - -Example on the A113 SoC: - -spdifout: audio-controller@480 { - compatible = "amlogic,axg-spdifout"; - reg = <0x0 0x480 0x0 0x50>; - #sound-dai-cells = <0>; - clocks = <&clkc_audio AUD_CLKID_SPDIFOUT>, - <&clkc_audio AUD_CLKID_SPDIFOUT_CLK>; - clock-names = "pclk", "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml new file mode 100644 index 000000000000..15be8dae9398 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-spdifout.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Audio AXG SPDIF Output + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + oneOf: + - const: amlogic,axg-spdifout + - items: + - enum: + - amlogic,g12a-spdifout + - amlogic,sm1-spdifout + - const: amlogic,axg-spdifout + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + items: + - description: Peripheral clock + - description: SPDIF output master clock + + clock-names: + items: + - const: pclk + - const: mclk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - "#sound-dai-cells" + - clocks + - clock-names + +allOf: + - $ref: dai-common.yaml# + + - if: + properties: + compatible: + contains: + enum: + - amlogic,g12a-spdifout + - amlogic,sm1-spdifout + then: + required: + - resets + + else: + properties: + resets: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + + audio-controller@480 { + compatible = "amlogic,axg-spdifout"; + reg = <0x480 0x50>; + #sound-dai-cells = <0>; + clocks = <&clkc_audio AUD_CLKID_SPDIFOUT>, + <&clkc_audio AUD_CLKID_SPDIFOUT_CLK>; + clock-names = "pclk", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt deleted file mode 100644 index 5996c0cd89c2..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt +++ /dev/null @@ -1,36 +0,0 @@ -* Amlogic Audio TDM formatters - -Required properties: -- compatible: 'amlogic,axg-tdmin' or - 'amlogic,axg-tdmout' or - 'amlogic,g12a-tdmin' or - 'amlogic,g12a-tdmout' or - 'amlogic,sm1-tdmin' or - 'amlogic,sm1-tdmout -- reg: physical base address of the controller and length of memory - mapped region. -- clocks: list of clock phandle, one for each entry clock-names. -- clock-names: should contain the following: - * "pclk" : peripheral clock. - * "sclk" : bit clock. - * "sclk_sel" : bit clock input multiplexer. - * "lrclk" : sample clock - * "lrclk_sel": sample clock input multiplexer - -Optional property: -- resets: phandle to the dedicated reset line of the tdm formatter. - -Example of TDMOUT_A on the S905X2 SoC: - -tdmout_a: audio-controller@500 { - compatible = "amlogic,axg-tdmout"; - reg = <0x0 0x500 0x0 0x40>; - resets = <&clkc_audio AUD_RESET_TDMOUT_A>; - clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>, - <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>, - <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>, - <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>, - <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>; - clock-names = "pclk", "sclk", "sclk_sel", - "lrclk", "lrclk_sel"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml new file mode 100644 index 000000000000..719ca8fc98c7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-tdm-formatters.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Audio AXG TDM formatters + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + compatible: + enum: + - amlogic,g12a-tdmout + - amlogic,sm1-tdmout + - amlogic,axg-tdmout + - amlogic,g12a-tdmin + - amlogic,sm1-tdmin + - amlogic,axg-tdmin + + clocks: + items: + - description: Peripheral clock + - description: Bit clock + - description: Bit clock input multiplexer + - description: Sample clock + - description: Sample clock input multiplexer + + clock-names: + items: + - const: pclk + - const: sclk + - const: sclk_sel + - const: lrclk + - const: lrclk_sel + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + +allOf: + - $ref: component-common.yaml# + + - if: + properties: + compatible: + contains: + enum: + - amlogic,g12a-tdmin + - amlogic,sm1-tdmin + - amlogic,g12a-tdmout + - amlogic,sm1-tdmout + then: + required: + - resets + + else: + properties: + resets: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h> + + audio-controller@500 { + compatible = "amlogic,g12a-tdmout"; + reg = <0x500 0x40>; + resets = <&clkc_audio AUD_RESET_TDMOUT_A>; + clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>, + <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>, + <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>, + <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>, + <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>; + clock-names = "pclk", "sclk", "sclk_sel", + "lrclk", "lrclk_sel"; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt deleted file mode 100644 index cabfb26a5f22..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt +++ /dev/null @@ -1,22 +0,0 @@ -* Amlogic Audio TDM Interfaces - -Required properties: -- compatible: 'amlogic,axg-tdm-iface' -- clocks: list of clock phandle, one for each entry clock-names. -- clock-names: should contain the following: - * "sclk" : bit clock. - * "lrclk": sample clock - * "mclk" : master clock - -> optional if the interface is in clock slave mode. -- #sound-dai-cells: must be 0. - -Example of TDM_A on the A113 SoC: - -tdmif_a: audio-controller@0 { - compatible = "amlogic,axg-tdm-iface"; - #sound-dai-cells = <0>; - clocks = <&clkc_audio AUD_CLKID_MST_A_MCLK>, - <&clkc_audio AUD_CLKID_MST_A_SCLK>, - <&clkc_audio AUD_CLKID_MST_A_LRCLK>; - clock-names = "mclk", "sclk", "lrclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml new file mode 100644 index 000000000000..320f0002649d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,axg-tdm-iface.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Audio TDM Interfaces + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: amlogic,axg-tdm-iface + + "#sound-dai-cells": + const: 0 + + clocks: + minItems: 2 + items: + - description: Bit clock + - description: Sample clock + - description: Master clock #optional + + clock-names: + minItems: 2 + items: + - const: sclk + - const: lrclk + - const: mclk + +required: + - compatible + - "#sound-dai-cells" + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/axg-audio-clkc.h> + + audio-controller { + compatible = "amlogic,axg-tdm-iface"; + #sound-dai-cells = <0>; + clocks = <&clkc_audio AUD_CLKID_MST_A_SCLK>, + <&clkc_audio AUD_CLKID_MST_A_LRCLK>, + <&clkc_audio AUD_CLKID_MST_A_MCLK>; + clock-names = "sclk", "lrclk", "mclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index 5b8d59245f82..b358fd601ed3 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -62,7 +62,7 @@ patternProperties: description: phandle of the CPU DAI patternProperties: - "^codec-[0-9]+$": + "^codec(-[0-9]+)?$": type: object additionalProperties: false description: |- diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index f5b8b6d13077..6b4e02a0695a 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -11,32 +11,24 @@ maintainers: select: false -allOf: - - $ref: /schemas/graph.yaml#/$defs/port-base - -properties: - prefix: - description: "device name prefix" - $ref: /schemas/types.yaml#/definitions/string - convert-rate: - $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate" - convert-channels: - $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels" - convert-sample-format: - $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format" +definitions: + port-base: + $ref: /schemas/graph.yaml#/$defs/port-base + properties: + convert-rate: + $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate" + convert-channels: + $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels" + convert-sample-format: + $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format" + mclk-fs: + $ref: "simple-card.yaml#/definitions/mclk-fs" -patternProperties: - "^endpoint(@[0-9a-f]+)?": + endpoint-base: $ref: /schemas/graph.yaml#/$defs/endpoint-base - unevaluatedProperties: false - properties: mclk-fs: - description: | - Multiplication factor between stream rate and codec mclk. - When defined, mclk-fs property defined in dai-link sub nodes are - ignored. - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: "simple-card.yaml#/definitions/mclk-fs" frame-inversion: description: dai-link uses frame clock inversion $ref: /schemas/types.yaml#/definitions/flag @@ -53,6 +45,15 @@ patternProperties: oneOf: - $ref: /schemas/types.yaml#/definitions/flag - $ref: /schemas/types.yaml#/definitions/phandle + clocks: + description: Indicates system clock + $ref: /schemas/types.yaml#/definitions/phandle + system-clock-frequency: + $ref: "simple-card.yaml#/definitions/system-clock-frequency" + system-clock-direction-out: + $ref: "simple-card.yaml#/definitions/system-clock-direction-out" + system-clock-fixed: + $ref: "simple-card.yaml#/definitions/system-clock-fixed" dai-format: description: audio format. @@ -100,4 +101,24 @@ patternProperties: minimum: 1 maximum: 64 + ports: + $ref: "#/definitions/port-base" + unevaluatedProperties: false + patternProperties: + "^port(@[0-9a-f]+)?$": + $ref: "#/definitions/port-base" + unevaluatedProperties: false + patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: "#/definitions/endpoint-base" + unevaluatedProperties: false + +allOf: + - $ref: "#/definitions/port-base" + +patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: "#/definitions/endpoint-base" + unevaluatedProperties: false + additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml new file mode 100644 index 000000000000..35eef7d818a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw88395.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW88395 Smart Audio Amplifier + +maintainers: + - Weidong Wang <wangweidong.a@awinic.com> + +description: + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10.25V + smart boost convert. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: awinic,aw88395 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - '#sound-dai-cells' + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@34 { + compatible = "awinic,aw88395"; + reg = <0x34>; + #sound-dai-cells = <0>; + reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml index 82062d80d700..18fb471aa891 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml @@ -22,6 +22,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + '#sound-dai-cells': description: The first cell indicating the audio interface. @@ -42,7 +45,7 @@ properties: Configures the peak current by monitoring the current through the boost FET. Range starts at 1600 mA and goes to a maximum of 4500 mA with increments of 50 mA. See section 4.3.6 of the datasheet for details. - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 1600 maximum: 4500 default: 4500 @@ -51,7 +54,7 @@ properties: description: Boost inductor value, expressed in nH. Valid values include 1000, 1200, 1500 and 2200. - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 1000 maximum: 2200 @@ -60,7 +63,7 @@ properties: Total equivalent boost capacitance on the VBST and VAMP pins, derated at 11 volts DC. The value must be rounded to the nearest integer and expressed in uF. - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 cirrus,asp-sdout-hiz: description: @@ -70,7 +73,7 @@ properties: 1 = Hi-Z during unused slots but logic 0 while all transmit channels disabled 2 = (Default) Logic 0 during unused slots, but Hi-Z while all transmit channels disabled 3 = Hi-Z during unused slots and while all transmit channels disabled - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 3 default: 2 @@ -84,7 +87,7 @@ properties: enable boost voltage. 0 = Internal Boost 1 = External Boost - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 1 @@ -109,7 +112,7 @@ properties: 1 = GPIO 2 = Sync 3 = MCLK input - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 3 @@ -136,7 +139,7 @@ properties: 3 = MCLK input 4 = Push-pull INTB (active low) 5 = Push-pull INT (active high) - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 5 @@ -176,21 +179,23 @@ unevaluatedProperties: false examples: - | + #include <dt-bindings/gpio/gpio.h> + spi { #address-cells = <1>; #size-cells = <0>; - cs35l41: cs35l41@2 { - #sound-dai-cells = <1>; - compatible = "cirrus,cs35l41"; - reg = <2>; - VA-supply = <&dummy_vreg>; - VP-supply = <&dummy_vreg>; - reset-gpios = <&gpio 110 0>; - - cirrus,boost-type = <0>; - cirrus,boost-peak-milliamp = <4500>; - cirrus,boost-ind-nanohenry = <1000>; - cirrus,boost-cap-microfarad = <15>; + cs35l41: speaker-amp@2 { + #sound-dai-cells = <1>; + compatible = "cirrus,cs35l41"; + reg = <2>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>; + + cirrus,boost-type = <0>; + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-cap-microfarad = <15>; }; }; diff --git a/Documentation/devicetree/bindings/sound/component-common.yaml b/Documentation/devicetree/bindings/sound/component-common.yaml new file mode 100644 index 000000000000..37766c5f3974 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/component-common.yaml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/component-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Audio Component Common Properties + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +properties: + sound-name-prefix: + $ref: /schemas/types.yaml#/definitions/string + description: | + Card implementing the routing property define the connection between + audio components as list of string pair. Component using the same + sink/source names may use this property to prepend the name of their + sinks/sources with the provided string. + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/dai-common.yaml b/Documentation/devicetree/bindings/sound/dai-common.yaml index d858eea73ed7..1aed2f0f1775 100644 --- a/Documentation/devicetree/bindings/sound/dai-common.yaml +++ b/Documentation/devicetree/bindings/sound/dai-common.yaml @@ -9,15 +9,10 @@ title: Digital Audio Interface Common Properties maintainers: - Jerome Brunet <jbrunet@baylibre.com> -properties: - sound-name-prefix: - $ref: /schemas/types.yaml#/definitions/string - description: | - Card implementing the routing property define the connection between - audio components as list of string pair. Component using the same - sink/source names may use this property to prepend the name of their - sinks/sources with the provided string. +allOf: + - $ref: component-common.yaml# +properties: '#sound-dai-cells': true additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml index 7e56337d8edc..088c26b001cc 100644 --- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml @@ -76,10 +76,14 @@ properties: minItems: 4 dmas: - maxItems: 2 + items: + - description: DMA controller phandle and request line for RX + - description: DMA controller phandle and request line for TX dma-names: - maxItems: 2 + items: + - const: rx + - const: tx interrupts: items: @@ -143,31 +147,6 @@ properties: allOf: - $ref: dai-common.yaml# - if: - properties: - compatible: - contains: - const: fsl,vf610-sai - then: - properties: - dmas: - items: - - description: DMA controller phandle and request line for TX - - description: DMA controller phandle and request line for RX - dma-names: - items: - - const: tx - - const: rx - else: - properties: - dmas: - items: - - description: DMA controller phandle and request line for RX - - description: DMA controller phandle and request line for TX - dma-names: - items: - - const: rx - - const: tx - - if: required: - fsl,sai-asynchronous then: @@ -199,9 +178,8 @@ examples: <&clks VF610_CLK_SAI2>, <&clks 0>, <&clks 0>; clock-names = "bus", "mclk1", "mclk2", "mclk3"; - dma-names = "tx", "rx"; - dmas = <&edma0 0 21>, - <&edma0 0 20>; + dma-names = "rx", "tx"; + dmas = <&edma0 0 20>, <&edma0 0 21>; big-endian; lsb-first; }; diff --git a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml index 223b8ea693dc..799b362ba498 100644 --- a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml @@ -21,6 +21,7 @@ properties: compatible: enum: - fsl,imx8mp-xcvr + - fsl,imx93-xcvr reg: items: diff --git a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml index 869b40363af8..0b1a01a4c14e 100644 --- a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml +++ b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml @@ -75,6 +75,18 @@ patternProperties: additionalProperties: false + platform: + description: Holds subnode which includes the phandle of q6apm platform device. + type: object + properties: + sound-dai: + maxItems: 1 + + required: + - sound-dai + + additionalProperties: false + required: - link-name - cpu diff --git a/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml new file mode 100644 index 000000000000..66993d378aaf --- /dev/null +++ b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/infineon,peb2466.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Infineon PEB2466 codec + +maintainers: + - Herve Codina <herve.codina@bootlin.com> + +description: | + The Infineon PEB2466 codec is a programmable DSP-based four channels codec + with filters capabilities. + + The time-slots used by the codec must be set and so, the properties + 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and + 'dai-tdm-slot-rx-mask' must be present in the sound card node for sub-nodes + that involve the codec. The codec uses one 8bit time-slot per channel. + 'dai-tdm-tdm-slot-with' must be set to 8. + + The PEB2466 codec also supports 28 gpios (signaling pins). + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml + - $ref: dai-common.yaml# + +properties: + compatible: + const: infineon,peb2466 + + reg: + description: + SPI device address. + maxItems: 1 + + clocks: + items: + - description: Master clock + + clock-names: + items: + - const: mclk + + spi-max-frequency: + maximum: 8192000 + + reset-gpios: + description: + GPIO used to reset the device. + maxItems: 1 + + firmware-name: + $ref: /schemas/types.yaml#/definitions/string + description: + Filters coefficients file to load. If this property is omitted, internal + filters are disabled. + + '#sound-dai-cells': + const: 0 + + '#gpio-cells': + const: 2 + + gpio-controller: true + +required: + - compatible + - reg + - '#sound-dai-cells' + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@0 { + compatible = "infineon,peb2466"; + reg = <0>; + spi-max-frequency = <8192000>; + reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + gpio-controller; + #gpio-cells = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml new file mode 100644 index 000000000000..b36c35e5da1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/irondevice,sma1303.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Iron Device SMA1303 Audio Amplifier + +maintainers: + - Kiseok Jo <kiseok.jo@irondevice.com> + +description: + SMA1303 digital class-D audio amplifier + with an integrated boost converter. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - irondevice,sma1303 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 1 + +required: + - compatible + - reg + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + amplifier@1e { + compatible = "irondevice,sma1303"; + reg = <0x1e>; + #sound-dai-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt deleted file mode 100644 index 39d640294c62..000000000000 --- a/Documentation/devicetree/bindings/sound/max98090.txt +++ /dev/null @@ -1,59 +0,0 @@ -MAX98090 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : "maxim,max98090" or "maxim,max98091". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC - -- clock-names: Should be "mclk" - -- #sound-dai-cells : should be 0. - -- maxim,dmic-freq: Frequency at which to clock DMIC - -- maxim,micbias: Micbias voltage applies to the analog mic, valid voltages value are: - 0 - 2.2v - 1 - 2.55v - 2 - 2.4v - 3 - 2.8v - -Pins on the device (for linking into audio routes): - - * MIC1 - * MIC2 - * DMICL - * DMICR - * IN1 - * IN2 - * IN3 - * IN4 - * IN5 - * IN6 - * IN12 - * IN34 - * IN56 - * HPL - * HPR - * SPKL - * SPKR - * RCVL - * RCVR - * MICBIAS - -Example: - -audio-codec@10 { - compatible = "maxim,max98090"; - reg = <0x10>; - interrupt-parent = <&gpio>; - interrupts = <TEGRA_GPIO(H, 4) IRQ_TYPE_LEVEL_HIGH>; -}; diff --git a/Documentation/devicetree/bindings/sound/max98095.txt b/Documentation/devicetree/bindings/sound/max98095.txt deleted file mode 100644 index 318a4c82f17f..000000000000 --- a/Documentation/devicetree/bindings/sound/max98095.txt +++ /dev/null @@ -1,22 +0,0 @@ -MAX98095 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : "maxim,max98095". - -- reg : The I2C address of the device. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC - -- clock-names: Should be "mclk" - -Example: - -max98095: codec@11 { - compatible = "maxim,max98095"; - reg = <0x11>; -}; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98090.yaml b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml new file mode 100644 index 000000000000..65e4c516912f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/maxim,max98090.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX98090/MAX98091 audio codecs + +maintainers: + - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> + +description: | + Pins on the device (for linking into audio routes): + MIC1, MIC2, DMICL, DMICR, IN1, IN2, IN3, IN4, IN5, IN6, IN12, IN34, IN56, + HPL, HPR, SPKL, SPKR, RCVL, RCVR, MICBIAS + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - maxim,max98090 + - maxim,max98091 + + reg: + maxItems: 1 + + clocks: + items: + - description: master clock + + clock-names: + items: + - const: mclk + + interrupts: + maxItems: 1 + + maxim,dmic-freq: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 2500000 + description: + DMIC clock frequency + + maxim,micbias: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + default: 3 + description: | + Micbias voltage applied to the analog mic, valid voltages value are: + 0 - 2.2v + 1 - 2.55v + 2 - 2.4v + 3 - 2.8v + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@10 { + compatible = "maxim,max98090"; + reg = <0x10>; + interrupt-parent = <&gpx3>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + clocks = <&i2s0 0>; + clock-names = "mclk"; + #sound-dai-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98095.yaml b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml new file mode 100644 index 000000000000..77544a9e1587 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/maxim,max98095.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX98095 audio codec + +maintainers: + - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - maxim,max98095 + + reg: + maxItems: 1 + + clocks: + items: + - description: master clock + + clock-names: + items: + - const: mclk + + '#sound-dai-cells': + const: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@11 { + compatible = "maxim,max98095"; + reg = <0x11>; + clocks = <&i2s0 0>; + clock-names = "mclk"; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml new file mode 100644 index 000000000000..82ccb32f08f2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt8188-afe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek AFE PCM controller for mt8188 + +maintainers: + - Trevor Wu <trevor.wu@mediatek.com> + +properties: + compatible: + const: mediatek,mt8188-afe + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: audiosys + + mediatek,topckgen: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the mediatek topckgen controller + + power-domains: + maxItems: 1 + + clocks: + items: + - description: 26M clock + - description: audio pll1 clock + - description: audio pll2 clock + - description: clock divider for i2si1_mck + - description: clock divider for i2si2_mck + - description: clock divider for i2so1_mck + - description: clock divider for i2so2_mck + - description: clock divider for dptx_mck + - description: a1sys hoping clock + - description: audio intbus clock + - description: audio hires clock + - description: audio local bus clock + - description: mux for dptx_mck + - description: mux for i2so1_mck + - description: mux for i2so2_mck + - description: mux for i2si1_mck + - description: mux for i2si2_mck + - description: audio 26m clock + + clock-names: + items: + - const: clk26m + - const: apll1 + - const: apll2 + - const: apll12_div0 + - const: apll12_div1 + - const: apll12_div2 + - const: apll12_div3 + - const: apll12_div9 + - const: a1sys_hp_sel + - const: aud_intbus_sel + - const: audio_h_sel + - const: audio_local_bus_sel + - const: dptx_m_sel + - const: i2so1_m_sel + - const: i2so2_m_sel + - const: i2si1_m_sel + - const: i2si2_m_sel + - const: adsp_audio_26m + + mediatek,etdm-in1-cowork-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + etdm modules can share the same external clock pin. Specify + which etdm clock source is required by this etdm in module. + enum: + - 1 # etdm2_in + - 2 # etdm1_out + - 3 # etdm2_out + + mediatek,etdm-in2-cowork-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + etdm modules can share the same external clock pin. Specify + which etdm clock source is required by this etdm in module. + enum: + - 0 # etdm1_in + - 2 # etdm1_out + - 3 # etdm2_out + + mediatek,etdm-out1-cowork-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + etdm modules can share the same external clock pin. Specify + which etdm clock source is required by this etdm out module. + enum: + - 0 # etdm1_in + - 1 # etdm2_in + - 3 # etdm2_out + + mediatek,etdm-out2-cowork-source: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + etdm modules can share the same external clock pin. Specify + which etdm clock source is required by this etdm out module. + enum: + - 0 # etdm1_in + - 1 # etdm2_in + - 2 # etdm1_out + +patternProperties: + "^mediatek,etdm-in[1-2]-chn-disabled$": + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 1 + maxItems: 16 + description: + This is a list of channel IDs which should be disabled. + By default, all data received from ETDM pins will be outputed to + memory. etdm in supports disable_out in direct mode(w/o interconn), + so user can disable the specified channels by the property. + uniqueItems: true + items: + minimum: 0 + maximum: 15 + + "^mediatek,etdm-in[1-2]-multi-pin-mode$": + type: boolean + description: if present, the etdm data mode is I2S. + + "^mediatek,etdm-out[1-3]-multi-pin-mode$": + type: boolean + description: if present, the etdm data mode is I2S. + +required: + - compatible + - reg + - interrupts + - resets + - reset-names + - mediatek,topckgen + - power-domains + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + afe@10b10000 { + compatible = "mediatek,mt8188-afe"; + reg = <0x10b10000 0x10000>; + interrupts = <GIC_SPI 822 IRQ_TYPE_LEVEL_HIGH 0>; + resets = <&watchdog 14>; + reset-names = "audiosys"; + mediatek,topckgen = <&topckgen>; + power-domains = <&spm 13>; //MT8188_POWER_DOMAIN_AUDIO + mediatek,etdm-in2-cowork-source = <2>; + mediatek,etdm-out2-cowork-source = <0>; + mediatek,etdm-in1-multi-pin-mode; + mediatek,etdm-in1-chn-disabled = /bits/ 8 <0x0 0x2>; + clocks = <&clk26m>, + <&apmixedsys 9>, //CLK_APMIXED_APLL1 + <&apmixedsys 10>, //CLK_APMIXED_APLL2 + <&topckgen 186>, //CLK_TOP_APLL12_CK_DIV0 + <&topckgen 187>, //CLK_TOP_APLL12_CK_DIV1 + <&topckgen 188>, //CLK_TOP_APLL12_CK_DIV2 + <&topckgen 189>, //CLK_TOP_APLL12_CK_DIV3 + <&topckgen 191>, //CLK_TOP_APLL12_CK_DIV9 + <&topckgen 83>, //CLK_TOP_A1SYS_HP + <&topckgen 31>, //CLK_TOP_AUD_INTBUS + <&topckgen 32>, //CLK_TOP_AUDIO_H + <&topckgen 69>, //CLK_TOP_AUDIO_LOCAL_BUS + <&topckgen 81>, //CLK_TOP_DPTX + <&topckgen 77>, //CLK_TOP_I2SO1 + <&topckgen 78>, //CLK_TOP_I2SO2 + <&topckgen 79>, //CLK_TOP_I2SI1 + <&topckgen 80>, //CLK_TOP_I2SI2 + <&adsp_audio26m 0>; //CLK_AUDIODSP_AUDIO26M + clock-names = "clk26m", + "apll1", + "apll2", + "apll12_div0", + "apll12_div1", + "apll12_div2", + "apll12_div3", + "apll12_div9", + "a1sys_hp_sel", + "aud_intbus_sel", + "audio_h_sel", + "audio_local_bus_sel", + "dptx_m_sel", + "i2so1_m_sel", + "i2so2_m_sel", + "i2si1_m_sel", + "i2si2_m_sel", + "adsp_audio_26m"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml new file mode 100644 index 000000000000..6640272b3f4f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt8188-mt6359.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT8188 ASoC sound card + +maintainers: + - Trevor Wu <trevor.wu@mediatek.com> + +properties: + compatible: + const: mediatek,mt8188-mt6359-evb + + 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 + sink/source pair of strings. Valid names could be the input or output + widgets of audio components, power supplies, MicBias of codec and the + software switch. + + mediatek,platform: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of MT8188 ASoC platform. + +patternProperties: + "^dai-link-[0-9]+$": + type: object + description: + Container for dai-link level properties and CODEC sub-nodes. + + properties: + link-name: + description: + This property corresponds to the name of the BE dai-link to which + we are going to update parameters in this node. + items: + enum: + - ADDA_BE + - DPTX_BE + - ETDM1_IN_BE + - ETDM2_IN_BE + - ETDM1_OUT_BE + - ETDM2_OUT_BE + - ETDM3_OUT_BE + - PCM1_BE + + codec: + description: Holds subnode which indicates codec dai. + type: object + additionalProperties: false + properties: + sound-dai: + minItems: 1 + maxItems: 2 + required: + - sound-dai + + additionalProperties: false + + required: + - link-name + - codec + +additionalProperties: false + +required: + - compatible + - mediatek,platform + +examples: + - | + sound { + compatible = "mediatek,mt8188-mt6359-evb"; + mediatek,platform = <&afe>; + pinctrl-names = "default"; + pinctrl-0 = <&aud_pins_default>; + audio-routing = + "Headphone", "Headphone L", + "Headphone", "Headphone R", + "AIN1", "Headset Mic"; + dai-link-0 { + link-name = "ETDM3_OUT_BE"; + + codec { + sound-dai = <&hdmi0>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml index 621022872c8d..651f61c7c25a 100644 --- a/Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml +++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/mchp,i2s-mcc.yaml# +$id: http://devicetree.org/schemas/sound/microchip,sama7g5-i2smcc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip I2S Multi-Channel Controller diff --git a/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml index c37b89d94c12..c4cf1e5ab84b 100644 --- a/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml +++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/microchip,pdmc.yaml# +$id: http://devicetree.org/schemas/sound/microchip,sama7g5-pdmc.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip Pulse Density Microphone Controller diff --git a/Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml index 70a47c6823b1..2f43c684ab88 100644 --- a/Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml +++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/mchp,spdifrx.yaml# +$id: http://devicetree.org/schemas/sound/microchip,sama7g5-spdifrx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip S/PDIF Rx Controller diff --git a/Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml index c383162140bb..4702c528700d 100644 --- a/Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml +++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/mchp,spdiftx.yaml# +$id: http://devicetree.org/schemas/sound/microchip,sama7g5-spdiftx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip S/PDIF Tx Controller diff --git a/Documentation/devicetree/bindings/sound/nau8822.txt b/Documentation/devicetree/bindings/sound/nau8822.txt deleted file mode 100644 index a471d162d4e5..000000000000 --- a/Documentation/devicetree/bindings/sound/nau8822.txt +++ /dev/null @@ -1,16 +0,0 @@ -NAU8822 audio CODEC - -This device supports I2C only. - -Required properties: - - - compatible : "nuvoton,nau8822" - - - reg : the I2C address of the device. - -Example: - -codec: nau8822@1a { - compatible = "nuvoton,nau8822"; - reg = <0x1a>; -}; diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml new file mode 100644 index 000000000000..65105402a53d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nuvoton,nau8822.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NAU8822 audio CODEC + +description: | + 24 bit stereo audio codec with speaker driver. + This device supports I2C/SPI. + +maintainers: + - David Lin <CTLIN0@nuvoton.com> + +properties: + compatible: + enum: + - nuvoton,nau8822 + + reg: + maxItems: 1 + + nuvoton,spk-btl: + description: + If set, configure the two loudspeaker outputs as a Bridge Tied Load output + to drive a high power external loudspeaker. + $ref: /schemas/types.yaml#/definitions/flag + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "nuvoton,nau8822"; + reg = <0x1a>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml index bb42220916b3..6cc8f86c7531 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml @@ -35,7 +35,7 @@ properties: clocks: minItems: 3 - maxItems: 7 + maxItems: 10 clock-names: minItems: 1 @@ -65,6 +65,9 @@ properties: power-domain-names: maxItems: 1 + required-opps: + maxItems: 1 + '#sound-dai-cells': const: 1 @@ -75,7 +78,7 @@ properties: const: 0 patternProperties: - "^dai-link@[0-9a-f]$": + "^dai-link@[0-9a-f]+$": type: object description: | LPASS CPU dai node for each I2S device or Soundwire device. Bindings of each node @@ -121,6 +124,8 @@ allOf: then: properties: + clocks: + maxItems: 3 clock-names: items: - const: ahbix-clk @@ -135,6 +140,9 @@ allOf: then: properties: + clocks: + minItems: 7 + maxItems: 7 clock-names: items: - const: ahbix-clk @@ -153,33 +161,31 @@ allOf: then: properties: + clocks: + minItems: 6 + maxItems: 6 clock-names: - oneOf: - - items: #for I2S - - const: pcnoc-sway-clk - - const: audio-core - - const: mclk0 - - const: pcnoc-mport-clk - - const: mi2s-bit-clk0 - - const: mi2s-bit-clk1 - - items: #for HDMI - - const: pcnoc-sway-clk - - const: audio-core - - const: pcnoc-mport-clk + items: + - const: pcnoc-sway-clk + - const: audio-core + - const: mclk0 + - const: pcnoc-mport-clk + - const: mi2s-bit-clk0 + - const: mi2s-bit-clk1 + reg: + minItems: 2 + maxItems: 2 reg-names: - anyOf: - - items: #for I2S - - const: lpass-lpaif - - items: #for I2S and HDMI - - const: lpass-hdmiif - - const: lpass-lpaif + items: + - const: lpass-hdmiif + - const: lpass-lpaif + interrupts: + minItems: 2 + maxItems: 2 interrupt-names: - anyOf: - - items: #for I2S - - const: lpass-irq-lpaif - - items: #for I2S and HDMI - - const: lpass-irq-lpaif - - const: lpass-irq-hdmi + items: + - const: lpass-irq-lpaif + - const: lpass-irq-hdmi required: - iommus - power-domains @@ -192,54 +198,44 @@ allOf: then: properties: + clocks: + minItems: 10 + maxItems: 10 clock-names: - oneOf: - - items: #for I2S - - const: aon_cc_audio_hm_h - - const: audio_cc_ext_mclk0 - - const: core_cc_sysnoc_mport_core - - const: core_cc_ext_if0_ibit - - const: core_cc_ext_if1_ibit - - items: #for Soundwire - - const: aon_cc_audio_hm_h - - const: audio_cc_codec_mem - - const: audio_cc_codec_mem0 - - const: audio_cc_codec_mem1 - - const: audio_cc_codec_mem2 - - const: aon_cc_va_mem0 - - items: #for HDMI - - const: core_cc_sysnoc_mport_core - + items: + - const: aon_cc_audio_hm_h + - const: audio_cc_ext_mclk0 + - const: core_cc_sysnoc_mport_core + - const: core_cc_ext_if0_ibit + - const: core_cc_ext_if1_ibit + - const: audio_cc_codec_mem + - const: audio_cc_codec_mem0 + - const: audio_cc_codec_mem1 + - const: audio_cc_codec_mem2 + - const: aon_cc_va_mem0 + reg: + minItems: 6 + maxItems: 6 reg-names: - anyOf: - - items: #for I2S - - const: lpass-lpaif - - items: #for I2S and HDMI - - const: lpass-hdmiif - - const: lpass-lpaif - - items: #for I2S, soundwire and HDMI - - const: lpass-hdmiif - - const: lpass-lpaif - - const: lpass-rxtx-cdc-dma-lpm - - const: lpass-rxtx-lpaif - - const: lpass-va-lpaif - - const: lpass-va-cdc-dma-lpm + items: + - const: lpass-hdmiif + - const: lpass-lpaif + - const: lpass-rxtx-cdc-dma-lpm + - const: lpass-rxtx-lpaif + - const: lpass-va-lpaif + - const: lpass-va-cdc-dma-lpm + interrupts: + minItems: 4 + maxItems: 4 interrupt-names: - anyOf: - - items: #for I2S - - const: lpass-irq-lpaif - - items: #for I2S and HDMI - - const: lpass-irq-lpaif - - const: lpass-irq-hdmi - - items: #for I2S, soundwire and HDMI - - const: lpass-irq-lpaif - - const: lpass-irq-hdmi - - const: lpass-irq-vaif - - const: lpass-irq-rxtxif + items: + - const: lpass-irq-lpaif + - const: lpass-irq-hdmi + - const: lpass-irq-vaif + - const: lpass-irq-rxtxif power-domain-names: - allOf: - - items: - - const: lcx + items: + - const: lcx required: - iommus diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index 26f0343b5aac..0a3c688ef1ec 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -36,7 +36,7 @@ properties: oneOf: - items: #for ADSP based platforms - const: mclk - - const: core + - const: macro - const: dcodec - items: #for ADSP bypass based platforms - const: mclk @@ -77,7 +77,7 @@ examples: clocks = <&aoncc 0>, <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; - clock-names = "mclk", "core", "dcodec"; + clock-names = "mclk", "macro", "dcodec"; clock-output-names = "fsgen"; qcom,dmic-sample-rate = <600000>; vdd-micb-supply = <&vreg_s4a_1p8>; diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml index a53c9ef938fa..cdbb4096fa44 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml @@ -17,7 +17,8 @@ properties: const: qcom,q6apm-dais iommus: - maxItems: 1 + minItems: 1 + maxItems: 2 required: - compatible diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 70080d04ddc9..262de7a60a73 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -15,16 +15,20 @@ description: properties: compatible: - enum: - - lenovo,yoga-c630-sndcard - - qcom,apq8016-sbc-sndcard - - qcom,db845c-sndcard - - qcom,msm8916-qdsp6-sndcard - - qcom,qrb5165-rb5-sndcard - - qcom,sc8280xp-sndcard - - qcom,sdm845-sndcard - - qcom,sm8250-sndcard - - qcom,sm8450-sndcard + oneOf: + - items: + - enum: + - lenovo,yoga-c630-sndcard + - qcom,db845c-sndcard + - const: qcom,sdm845-sndcard + - enum: + - qcom,apq8016-sbc-sndcard + - qcom,msm8916-qdsp6-sndcard + - qcom,qrb5165-rb5-sndcard + - qcom,sc8280xp-sndcard + - qcom,sdm845-sndcard + - qcom,sm8250-sndcard + - qcom,sm8450-sndcard audio-routing: $ref: /schemas/types.yaml#/definitions/non-unique-string-array diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml index 184e8ccbdd13..ea09590bfa30 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml @@ -28,7 +28,9 @@ properties: description: GPIO spec for reset line to use maxItems: 1 - slim-ifc-dev: true + slim-ifc-dev: + description: IFC device interface + $ref: /schemas/types.yaml#/definitions/phandle clocks: maxItems: 1 @@ -147,21 +149,49 @@ patternProperties: required: - compatible - reg - - reset-gpios - - slim-ifc-dev - - interrupts - - interrupt-controller - - clock-frequency - - clock-output-names - - qcom,micbias1-microvolt - - qcom,micbias2-microvolt - - qcom,micbias3-microvolt - - qcom,micbias4-microvolt - - "#interrupt-cells" - - "#clock-cells" - - "#sound-dai-cells" - - "#address-cells" - - "#size-cells" + +allOf: + - if: + required: + - slim-ifc-dev + then: + required: + - reset-gpios + - slim-ifc-dev + - interrupt-controller + - clock-frequency + - clock-output-names + - qcom,micbias1-microvolt + - qcom,micbias2-microvolt + - qcom,micbias3-microvolt + - qcom,micbias4-microvolt + - "#interrupt-cells" + - "#clock-cells" + - "#sound-dai-cells" + - "#address-cells" + - "#size-cells" + oneOf: + - required: + - interrupts-extended + - required: + - interrupts + else: + properties: + reset-gpios: false + slim-ifc-dev: false + interrupts: false + interrupt-controller: false + clock-frequency: false + clock-output-names: false + qcom,micbias1-microvolt: false + qcom,micbias2-microvolt: false + qcom,micbias3-microvolt: false + qcom,micbias4-microvolt: false + "#interrupt-cells": false + "#clock-cells": false + "#sound-dai-cells": false + "#address-cells": false + "#size-cells": false additionalProperties: false diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml index d702b489320f..ac03672ebf6d 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml @@ -15,6 +15,9 @@ description: | Their primary operating mode uses a SoundWire digital audio interface. This binding is for SoundWire interface. +allOf: + - $ref: dai-common.yaml# + properties: compatible: const: sdw10217201000 @@ -39,7 +42,7 @@ required: - "#thermal-sensor-cells" - "#sound-dai-cells" -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml new file mode 100644 index 000000000000..a2b92dba5529 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/renesas,idt821034.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas IDT821034 codec device + +maintainers: + - Herve Codina <herve.codina@bootlin.com> + +description: | + The IDT821034 codec is a four channel PCM codec with onchip filters and + programmable gain setting. + + The time-slots used by the codec must be set and so, the properties + 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and + 'dai-tdm-slot-rx-mask' must be present in the ALSA sound card node for + sub-nodes that involve the codec. The codec uses one 8bit time-slot per + channel. + 'dai-tdm-tdm-slot-with' must be set to 8. + + The IDT821034 codec also supports 5 gpios (SLIC signals) per channel. + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - $ref: dai-common.yaml# + +properties: + compatible: + const: renesas,idt821034 + + reg: + description: + SPI device address. + maxItems: 1 + + spi-max-frequency: + maximum: 8192000 + + spi-cpha: true + + '#sound-dai-cells': + const: 0 + + '#gpio-cells': + const: 2 + + gpio-controller: true + +required: + - compatible + - reg + - spi-cpha + - '#sound-dai-cells' + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@0 { + compatible = "renesas,idt821034"; + reg = <0>; + spi-max-frequency = <8192000>; + spi-cpha; + #sound-dai-cells = <0>; + gpio-controller; + #gpio-cells = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index cb90463c7297..12ccf29338d9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -18,8 +18,7 @@ properties: - enum: - renesas,rcar_sound-r8a7778 # R-Car M1A - renesas,rcar_sound-r8a7779 # R-Car H1 - - enum: - - renesas,rcar_sound-gen1 + - const: renesas,rcar_sound-gen1 # for Gen2 SoC - items: - enum: @@ -32,8 +31,7 @@ properties: - renesas,rcar_sound-r8a7791 # R-Car M2-W - renesas,rcar_sound-r8a7793 # R-Car M2-N - renesas,rcar_sound-r8a7794 # R-Car E2 - - enum: - - renesas,rcar_sound-gen2 + - const: renesas,rcar_sound-gen2 # for Gen3 SoC - items: - enum: @@ -47,14 +45,16 @@ properties: - renesas,rcar_sound-r8a77965 # R-Car M3-N - renesas,rcar_sound-r8a77990 # R-Car E3 - renesas,rcar_sound-r8a77995 # R-Car D3 - - enum: - - renesas,rcar_sound-gen3 - # for Generic + - const: renesas,rcar_sound-gen3 + # for Gen4 SoC - items: - - enum: - - renesas,rcar_sound-gen1 - - renesas,rcar_sound-gen2 - - renesas,rcar_sound-gen3 + - const: renesas,rcar_sound-r8a779g0 # R-Car V4H + - const: renesas,rcar_sound-gen4 + # for Generic + - enum: + - renesas,rcar_sound-gen1 + - renesas,rcar_sound-gen2 + - renesas,rcar_sound-gen3 reg: minItems: 1 @@ -68,6 +68,7 @@ properties: description: | it must be 0 if your system is using single DAI it must be 1 if your system is using multi DAIs + This is used on simple-audio-card enum: [0, 1] "#clock-cells": @@ -113,15 +114,34 @@ properties: - pattern: '^clk_(a|b|c|i)$' ports: - $ref: /schemas/graph.yaml#/properties/ports + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false patternProperties: '^port(@[0-9a-f]+)?$': - $ref: audio-graph-port.yaml# + $ref: audio-graph-port.yaml#/definitions/port-base unevaluatedProperties: false + patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: audio-graph-port.yaml#/definitions/endpoint-base + properties: + playback: + $ref: /schemas/types.yaml#/definitions/phandle-array + capture: + $ref: /schemas/types.yaml#/definitions/phandle-array + unevaluatedProperties: false port: - $ref: audio-graph-port.yaml# + $ref: audio-graph-port.yaml#/definitions/port-base unevaluatedProperties: false + patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: audio-graph-port.yaml#/definitions/endpoint-base + properties: + playback: + $ref: /schemas/types.yaml#/definitions/phandle-array + capture: + $ref: /schemas/types.yaml#/definitions/phandle-array + unevaluatedProperties: false rcar_sound,dvc: description: DVC subnode. @@ -178,10 +198,6 @@ properties: enum: - tx - rx - required: - - interrupts - - dmas - - dma-names additionalProperties: false rcar_sound,ssiu: @@ -240,8 +256,6 @@ properties: $ref: /schemas/types.yaml#/definitions/flag required: - interrupts - - dmas - - dma-names additionalProperties: false # For DAI base @@ -271,7 +285,6 @@ required: - reg-names - clocks - clock-names - - "#sound-dai-cells" allOf: - $ref: dai-common.yaml# @@ -285,7 +298,6 @@ allOf: reg: maxItems: 3 reg-names: - maxItems: 3 items: enum: - scu @@ -294,9 +306,8 @@ allOf: else: properties: reg: - maxItems: 5 + minItems: 5 reg-names: - maxItems: 5 items: enum: - scu diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index ff1228713f7e..0c398581d52b 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -20,6 +20,9 @@ Optional properties: - realtek,in3-differential Boolean. Indicate MIC1/2/3 input are differential, rather than single-ended. +- realtek,lout-differential + Boolean. Indicate LOUT output is differential, rather than stereo. + - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. - realtek,dmic1-data-pin diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml index 7b4e08ddef6a..7774543b8819 100644 --- a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml @@ -43,9 +43,10 @@ properties: type: object properties: sound-dai: + minItems: 1 items: - - description: phandle of the MAX98090 CODEC - description: phandle of the HDMI IP block node + - description: phandle of the MAX98090 CODEC samsung,audio-routing: $ref: /schemas/types.yaml#/definitions/non-unique-string-array diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml index 8d5dcf9cd43e..30b3b6e9824b 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml @@ -37,12 +37,20 @@ properties: samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports stereo channels. Exynos7 I2S1 upgraded to 5.1 multichannel with slightly modified bit offsets. + + tesla,fsd-i2s: for 8/16/24bit stereo channel I2S for playback and + capture, secondary FIFO using external DMA, s/w reset control, + internal mux for root clock source with all root clock sampling + frequencies supported by Exynos7 I2S and 7.1 channel TDM support + for playback and capture TDM (Time division multiplexing) to allow + transfer of multiple channel audio data on single data line. enum: - samsung,s3c6410-i2s - samsung,s5pv210-i2s - samsung,exynos5420-i2s - samsung,exynos7-i2s - samsung,exynos7-i2s1 + - tesla,fsd-i2s '#address-cells': const: 1 @@ -67,9 +75,6 @@ properties: - const: rx - const: tx-sec - assigned-clock-parents: true - assigned-clocks: true - clocks: minItems: 1 maxItems: 3 diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml index ed19899bc94b..f0d81bfe2598 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.yaml +++ b/Documentation/devicetree/bindings/sound/simple-card.yaml @@ -205,6 +205,8 @@ patternProperties: $ref: "#/definitions/dai" "^simple-audio-card,codec(@[0-9a-f]+)?$": $ref: "#/definitions/dai" + "^simple-audio-card,plat(@[0-9a-f]+)?$": + $ref: "#/definitions/dai" "^simple-audio-card,dai-link(@[0-9a-f]+)?$": description: | @@ -215,6 +217,10 @@ patternProperties: reg: maxItems: 1 + "#address-cells": + const: 1 + "#size-cells": + const: 0 # common properties frame-master: $ref: "#/definitions/frame-master" @@ -244,9 +250,9 @@ patternProperties: maxItems: 1 patternProperties: - "^cpu(@[0-9a-f]+)?": + "^cpu(-[0-9]+)?$": $ref: "#/definitions/dai" - "^codec(@[0-9a-f]+)?": + "^codec(-[0-9]+)?$": $ref: "#/definitions/dai" additionalProperties: false @@ -462,16 +468,16 @@ examples: convert-channels = <8>; /* TDM Split */ - sndcpu1: cpu0 { + sndcpu1: cpu-0 { sound-dai = <&rcar_sound 1>; }; - cpu1 { + cpu-1 { sound-dai = <&rcar_sound 2>; }; - cpu2 { + cpu-2 { sound-dai = <&rcar_sound 3>; }; - cpu3 { + cpu-3 { sound-dai = <&rcar_sound 4>; }; codec { diff --git a/Documentation/devicetree/bindings/sound/tas5720.txt b/Documentation/devicetree/bindings/sound/tas5720.txt index df99ca9451b0..7d851ae2bba2 100644 --- a/Documentation/devicetree/bindings/sound/tas5720.txt +++ b/Documentation/devicetree/bindings/sound/tas5720.txt @@ -6,11 +6,13 @@ audio playback. For more product information please see the links below: https://www.ti.com/product/TAS5720L https://www.ti.com/product/TAS5720M +https://www.ti.com/product/TAS5720A-Q1 https://www.ti.com/product/TAS5722L Required properties: - compatible : "ti,tas5720", + "ti,tas5720a-q1", "ti,tas5722" - reg : I2C slave address - dvdd-supply : phandle to a 3.3-V supply for the digital circuitry diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt deleted file mode 100644 index a02ecaab5183..000000000000 --- a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt +++ /dev/null @@ -1,56 +0,0 @@ -Texas Instruments pcm3168a DT bindings - -This driver supports both SPI and I2C bus access for this codec - -Required properties: - - - compatible: "ti,pcm3168a" - - - clocks : Contains an entry for each entry in clock-names - - - clock-names : Includes the following entries: - "scki" The system clock - - - VDD1-supply : Digital power supply regulator 1 (+3.3V) - - - VDD2-supply : Digital power supply regulator 2 (+3.3V) - - - VCCAD1-supply : ADC power supply regulator 1 (+5V) - - - VCCAD2-supply : ADC power supply regulator 2 (+5V) - - - VCCDA1-supply : DAC power supply regulator 1 (+5V) - - - VCCDA2-supply : DAC power supply regulator 2 (+5V) - -For required properties on SPI/I2C, consult SPI/I2C device tree documentation - -Optional properties: - - - reset-gpios : Optional reset gpio line connected to RST pin of the codec. - The RST line is low active: - RST = low: device power-down - RST = high: device is enabled - -Examples: - -i2c0: i2c0@0 { - - ... - - pcm3168a: audio-codec@44 { - compatible = "ti,pcm3168a"; - reg = <0x44>; - reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; - clocks = <&clk_core CLK_AUDIO>; - clock-names = "scki"; - VDD1-supply = <&supply3v3>; - VDD2-supply = <&supply3v3>; - VCCAD1-supply = <&supply5v0>; - VCCAD2-supply = <&supply5v0>; - VCCDA1-supply = <&supply5v0>; - VCCDA2-supply = <&supply5v0>; - pinctrl-names = "default"; - pinctrl-0 = <&dac_clk_pin>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml b/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml new file mode 100644 index 000000000000..b6a4360ab845 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,pcm3168a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments PCM3168A Audio Codec + +maintainers: + - Damien Horsley <Damien.Horsley@imgtec.com> + - Geert Uytterhoeven <geert+renesas@glider.be> + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + +description: + The Texas Instruments PCM3168A is a 24-bit Multi-channel Audio CODEC with + 96/192kHz sampling rate, supporting both SPI and I2C bus access. + +properties: + compatible: + const: ti,pcm3168a + + reg: + maxItems: 1 + + clocks: + items: + - description: System clock input + + clock-names: + items: + - const: scki + + reset-gpios: + items: + - description: | + GPIO line connected to the active-low RST pin of the codec. + RST = low: device power-down + RST = high: device is enabled + + "#sound-dai-cells": + enum: [0, 1] + + VDD1-supply: + description: Digital power supply regulator 1 (+3.3V) + + VDD2-supply: + description: Digital power supply regulator 2 (+3.3V) + + VCCAD1-supply: + description: ADC power supply regulator 1 (+5V) + + VCCAD2-supply: + description: ADC power supply regulator 2 (+5V) + + VCCDA1-supply: + description: DAC power supply regulator 1 (+5V) + + VCCDA2-supply: + description: DAC power supply regulator 2 (+5V) + + ports: + $ref: audio-graph-port.yaml#/definitions/port-base + properties: + port@0: + $ref: audio-graph-port.yaml# + description: Audio input port. + + port@1: + $ref: audio-graph-port.yaml# + description: Audio output port. + +required: + - compatible + - reg + - clocks + - clock-names + - VDD1-supply + - VDD2-supply + - VCCAD1-supply + - VCCAD2-supply + - VCCDA1-supply + - VCCDA2-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pcm3168a: audio-codec@44 { + compatible = "ti,pcm3168a"; + reg = <0x44>; + reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; + clocks = <&clk_core 42>; + clock-names = "scki"; + VDD1-supply = <&supply3v3>; + VDD2-supply = <&supply3v3>; + VCCAD1-supply = <&supply5v0>; + VCCAD2-supply = <&supply5v0>; + VCCDA1-supply = <&supply5v0>; + VCCDA2-supply = <&supply5v0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml new file mode 100644 index 000000000000..e8ca9f3369f8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml @@ -0,0 +1,165 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +# Copyright (C) 2022 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tlv320aic3x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TLV320AIC3x Codec + +description: | + TLV320AIC3x are a series of low-power stereo audio codecs with stereo + headphone amplifier, as well as multiple inputs and outputs programmable in + single-ended or fully differential configurations. + + The serial control bus supports SPI or I2C protocols, while the serial audio + data bus is programmable for I2S, left/right-justified, DSP, or TDM modes. + + The following pins can be referred in the sound node's audio routing property: + + CODEC output pins: + LLOUT + RLOUT + MONO_LOUT + HPLOUT + HPROUT + HPLCOM + HPRCOM + + CODEC input pins for TLV320AIC3104: + MIC2L + MIC2R + LINE1L + LINE1R + + CODEC input pins for other compatible codecs: + MIC3L + MIC3R + LINE1L + LINE2L + LINE1R + LINE2R + +maintainers: + - Jai Luthra <j-luthra@ti.com> + +properties: + compatible: + enum: + - ti,tlv320aic3x + - ti,tlv320aic33 + - ti,tlv320aic3007 + - ti,tlv320aic3106 + - ti,tlv320aic3104 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: + GPIO specification for the active low RESET input. + + gpio-reset: + maxItems: 1 + description: + Deprecated, please use reset-gpios instead. + deprecated: true + + ai3x-gpio-func: + description: AIC3X_GPIO1 & AIC3X_GPIO2 Functionality + $ref: /schemas/types.yaml#/definitions/uint32-array + maxItems: 2 + + ai3x-micbias-vg: + description: MicBias required voltage. If node is omitted then MicBias is powered down. + $ref: /schemas/types.yaml#/definitions/uint32 + oneOf: + - const: 1 + description: MICBIAS output is powered to 2.0V. + - const: 2 + description: MICBIAS output is powered to 2.5V. + - const: 3 + description: MICBIAS output is connected to AVDD. + + ai3x-ocmv: + description: Output Common-Mode Voltage selection. + $ref: /schemas/types.yaml#/definitions/uint32 + oneOf: + - const: 0 + description: 1.35V + - const: 1 + description: 1.5V + - const: 2 + description: 1.65V + - const: 3 + description: 1.8V + + AVDD-supply: + description: Analog DAC voltage. + + IOVDD-supply: + description: I/O voltage. + + DRVDD-supply: + description: ADC analog and output driver voltage. + + DVDD-supply: + description: Digital core voltage. + + '#sound-dai-cells': + const: 0 + + clocks: + maxItems: 1 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + tlv320aic3x_i2c: audio-codec@1b { + compatible = "ti,tlv320aic3x"; + reg = <0x1b>; + + reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; + + AVDD-supply = <®ulator>; + IOVDD-supply = <®ulator>; + DRVDD-supply = <®ulator>; + DVDD-supply = <®ulator>; + }; + }; + + - | + #include <dt-bindings/gpio/gpio.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + tlv320aic3x_spi: audio-codec@0 { + compatible = "ti,tlv320aic3x"; + reg = <0>; /* CS number */ + #sound-dai-cells = <0>; + + AVDD-supply = <®ulator>; + IOVDD-supply = <®ulator>; + DRVDD-supply = <®ulator>; + DVDD-supply = <®ulator>; + ai3x-ocmv = <0>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt deleted file mode 100644 index 20931a63fd64..000000000000 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ /dev/null @@ -1,97 +0,0 @@ -Texas Instruments - tlv320aic3x Codec module - -The tlv320aic3x serial control bus communicates through both I2C and SPI bus protocols - -Required properties: - -- compatible - "string" - One of: - "ti,tlv320aic3x" - Generic TLV320AIC3x device - "ti,tlv320aic33" - TLV320AIC33 - "ti,tlv320aic3007" - TLV320AIC3007 - "ti,tlv320aic3106" - TLV320AIC3106 - "ti,tlv320aic3104" - TLV320AIC3104 - - -- reg - <int> - I2C slave address - - -Optional properties: - -- reset-gpios - GPIO specification for the active low RESET input. -- ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality - - Not supported on tlv320aic3104 -- ai3x-micbias-vg - MicBias Voltage required. - 1 - MICBIAS output is powered to 2.0V, - 2 - MICBIAS output is powered to 2.5V, - 3 - MICBIAS output is connected to AVDD, - If this node is not mentioned or if the value is incorrect, then MicBias - is powered down. -- ai3x-ocmv - Output Common-Mode Voltage selection: - 0 - 1.35V, - 1 - 1.5V, - 2 - 1.65V, - 3 - 1.8V -- AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the - device as covered in Documentation/devicetree/bindings/regulator/regulator.txt - -Deprecated properties: - -- gpio-reset - gpio pin number used for codec reset - -CODEC output pins: - * LLOUT - * RLOUT - * MONO_LOUT - * HPLOUT - * HPROUT - * HPLCOM - * HPRCOM - -CODEC input pins for TLV320AIC3104: - * MIC2L - * MIC2R - * LINE1L - * LINE1R - -CODEC input pins for other compatible codecs: - * MIC3L - * MIC3R - * LINE1L - * LINE2L - * LINE1R - * LINE2R - -The pins can be used in referring sound node's audio-routing property. - -I2C example: - -#include <dt-bindings/gpio/gpio.h> - -tlv320aic3x: tlv320aic3x@1b { - compatible = "ti,tlv320aic3x"; - reg = <0x1b>; - - reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; - - AVDD-supply = <®ulator>; - IOVDD-supply = <®ulator>; - DRVDD-supply = <®ulator>; - DVDD-supply = <®ulator>; -}; - -SPI example: - -spi0: spi@f0000000 { - tlv320aic3x: codec@0 { - compatible = "ti,tlv320aic3x"; - reg = <0>; /* CS number */ - #sound-dai-cells = <0>; - spi-max-frequency = <1000000>; - - AVDD-supply = <®ulator>; - IOVDD-supply = <®ulator>; - DRVDD-supply = <®ulator>; - DVDD-supply = <®ulator>; - ai3x-ocmv = <0>; - }; -}; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 5c3c6abb983b..85bb190cdd2f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -645,6 +645,8 @@ patternProperties: description: Inverse Path "^iom,.*": description: Iomega Corporation + "^irondevice,.*": + description: Iron Device Corporation "^isee,.*": description: ISEE 2007 S.L. "^isil,.*": diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index 21ab5e6f7062..5f31fa5e2435 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -70,7 +70,7 @@ dsp_map PCM device number maps assigned to the 1st OSS device; Default: 0 adsp_map - PCM device number maps assigned to the 2st OSS device; + PCM device number maps assigned to the 2nd OSS device; Default: 1 nonblock_open Don't block opening busy PCM devices; @@ -97,7 +97,7 @@ midi_map MIDI device number maps assigned to the 1st OSS device; Default: 0 amidi_map - MIDI device number maps assigned to the 2st OSS device; + MIDI device number maps assigned to the 2nd OSS device; Default: 1 Module snd-soc-core @@ -727,9 +727,9 @@ Module for EMU10K1/EMU10k2 based PCI sound cards. * Sound Blaster Audigy extin - bitmap of available external inputs for FX8010 (see bellow) + bitmap of available external inputs for FX8010 (see below) extout - bitmap of available external outputs for FX8010 (see bellow) + bitmap of available external outputs for FX8010 (see below) seq_ports allocated sequencer ports (4 by default) max_synth_voices diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst index f3f4640ee2af..c506f8d16f2e 100644 --- a/Documentation/sound/cards/audigy-mixer.rst +++ b/Documentation/sound/cards/audigy-mixer.rst @@ -17,7 +17,7 @@ Digital mixer controls ====================== These controls are built using the DSP instructions. They offer extended -functionality. Only the default build-in code in the ALSA driver is described +functionality. Only the default built-in code in the ALSA driver is described here. Note that the controls work as attenuators: the maximum value is the neutral position leaving the signal unchanged. Note that if the same destination is mentioned in multiple controls, the signal is accumulated and can be wrapped diff --git a/Documentation/sound/cards/maya44.rst b/Documentation/sound/cards/maya44.rst index bf09a584b443..ab973f66c973 100644 --- a/Documentation/sound/cards/maya44.rst +++ b/Documentation/sound/cards/maya44.rst @@ -156,7 +156,7 @@ IEC958 Output S/PDIF should output the same signal as channel 3+4. [untested!] -Digitial output selectors +Digital output selectors These switches allow a direct digital routing from the ADCs to the DACs. Each switch determines where the digital input data to one of the DACs comes from. They are not supported by the ESI windows driver. diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst index 2ce41d3822d8..357fcd619d39 100644 --- a/Documentation/sound/cards/sb-live-mixer.rst +++ b/Documentation/sound/cards/sb-live-mixer.rst @@ -31,7 +31,7 @@ Digital mixer controls ====================== These controls are built using the DSP instructions. They offer extended -functionality. Only the default build-in code in the ALSA driver is described +functionality. Only the default built-in code in the ALSA driver is described here. Note that the controls work as attenuators: the maximum value is the neutral position leaving the signal unchanged. Note that if the same destination is mentioned in multiple controls, the signal is accumulated and can be wrapped diff --git a/Documentation/sound/designs/jack-controls.rst b/Documentation/sound/designs/jack-controls.rst index ae25b1531bb0..e8a18f126a63 100644 --- a/Documentation/sound/designs/jack-controls.rst +++ b/Documentation/sound/designs/jack-controls.rst @@ -8,7 +8,7 @@ Why we need Jack kcontrols ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...) to user space. This means userspace applications like pulseaudio can switch off headphones and switch on speakers when no headphones are -pluged in. +plugged in. The old ALSA jack code only created input devices for each registered jack. These jack input devices are not readable by userspace devices diff --git a/Documentation/sound/designs/seq-oss.rst b/Documentation/sound/designs/seq-oss.rst index e82ffe0e7f43..ec6304a07441 100644 --- a/Documentation/sound/designs/seq-oss.rst +++ b/Documentation/sound/designs/seq-oss.rst @@ -96,7 +96,7 @@ if you use an AWE64 card, you'll see like the following: Number of synth devices: 1 synth 0: [EMU8000] type 0x1 : subtype 0x20 : voices 32 - capabilties : ioctl enabled / load_patch enabled + capabilities : ioctl enabled / load_patch enabled Number of MIDI devices: 3 midi 0: [Emu8000 Port-0] ALSA port 65:0 diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index d118b6fe269b..3432feb0fa33 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -500,7 +500,7 @@ add_jack_modes (bool) change the headphone amp and mic bias VREF capabilities power_save_node (bool) advanced power management for each widget, controlling the power - sate (D0/D3) of each widget node depending on the actual pin and + state (D0/D3) of each widget node depending on the actual pin and stream states power_down_unused (bool) power down the unused widgets, a subset of power_save_node, and diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index 07a620c5ca74..5c9523b7d55c 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -1720,16 +1720,16 @@ Typically, you'll have a hardware descriptor as below: - ``rate_min`` and ``rate_max`` define the minimum and maximum sample rate. This should correspond somehow to ``rates`` bits. -- ``channel_min`` and ``channel_max`` define, as you might already +- ``channels_min`` and ``channels_max`` define, as you might already expected, the minimum and maximum number of channels. - ``buffer_bytes_max`` defines the maximum buffer size in bytes. There is no ``buffer_bytes_min`` field, since it can be calculated from the minimum period size and the minimum number of - periods. Meanwhile, ``period_bytes_min`` and define the minimum and - maximum size of the period in bytes. ``periods_max`` and - ``periods_min`` define the maximum and minimum number of periods in - the buffer. + periods. Meanwhile, ``period_bytes_min`` and ``period_bytes_max`` + define the minimum and maximum size of the period in bytes. + ``periods_max`` and ``periods_min`` define the maximum and minimum + number of periods in the buffer. The “period” is a term that corresponds to a fragment in the OSS world. The period defines the size at which a PCM interrupt is diff --git a/MAINTAINERS b/MAINTAINERS index fbff490379ad..440c7141e448 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10017,6 +10017,13 @@ L: linux-iio@vger.kernel.org S: Maintained F: drivers/iio/pressure/dps310.c +INFINEON PEB2466 ASoC CODEC +M: Herve Codina <herve.codina@bootlin.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/infineon,peb2466.yaml +F: sound/soc/codecs/peb2466.c + INFINIBAND SUBSYSTEM M: Jason Gunthorpe <jgg@nvidia.com> M: Leon Romanovsky <leonro@nvidia.com> @@ -10785,6 +10792,13 @@ M: David Sterba <dsterba@suse.com> S: Odd Fixes F: drivers/tty/ipwireless/ +IRON DEVICE AUDIO CODEC DRIVERS +M: Kiseok Jo <kiseok.jo@irondevice.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/irondevice,* +F: sound/soc/codecs/sma* + IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Marc Zyngier <maz@kernel.org> S: Maintained @@ -17690,6 +17704,13 @@ F: Documentation/devicetree/bindings/net/renesas,*.yaml F: drivers/net/ethernet/renesas/ F: include/linux/sh_eth.h +RENESAS IDT821034 ASoC CODEC +M: Herve Codina <herve.codina@bootlin.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/renesas,idt821034.yaml +F: sound/soc/codecs/idt821034.c + RENESAS R-CAR GYROADC DRIVER M: Marek Vasut <marek.vasut@gmail.com> L: linux-iio@vger.kernel.org diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 958aa4662ccb..2c16ee8fd842 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -111,6 +111,7 @@ struct inbound_transaction_resource { struct client_resource resource; struct fw_card *card; struct fw_request *request; + bool is_fcp; void *data; size_t length; }; @@ -643,19 +644,14 @@ static int ioctl_send_request(struct client *client, union ioctl_arg *arg) client->device->max_speed); } -static inline bool is_fcp_request(struct fw_request *request) -{ - return request == NULL; -} - static void release_request(struct client *client, struct client_resource *resource) { struct inbound_transaction_resource *r = container_of(resource, struct inbound_transaction_resource, resource); - if (is_fcp_request(r->request)) - kfree(r->data); + if (r->is_fcp) + fw_request_put(r->request); else fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); @@ -669,15 +665,20 @@ static void handle_request(struct fw_card *card, struct fw_request *request, void *payload, size_t length, void *callback_data) { struct address_handler_resource *handler = callback_data; + bool is_fcp = is_in_fcp_region(offset, length); struct inbound_transaction_resource *r; struct inbound_transaction_event *e; size_t event_size0; - void *fcp_frame = NULL; int ret; /* card may be different from handler->client->device->card */ fw_card_get(card); + // Extend the lifetime of data for request so that its payload is safely accessible in + // the process context for the client. + if (is_fcp) + fw_request_get(request); + r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); if (r == NULL || e == NULL) @@ -685,21 +686,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r->card = card; r->request = request; + r->is_fcp = is_fcp; r->data = payload; r->length = length; - if (is_fcp_request(request)) { - /* - * FIXME: Let core-transaction.c manage a - * single reference-counted copy? - */ - fcp_frame = kmemdup(payload, length, GFP_ATOMIC); - if (fcp_frame == NULL) - goto failed; - - r->data = fcp_frame; - } - r->resource.release = release_request; ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); if (ret < 0) @@ -741,10 +731,11 @@ static void handle_request(struct fw_card *card, struct fw_request *request, failed: kfree(r); kfree(e); - kfree(fcp_frame); - if (!is_fcp_request(request)) + if (!is_fcp) fw_send_response(card, request, RCODE_CONFLICT_ERROR); + else + fw_request_put(request); fw_card_put(card); } @@ -819,19 +810,19 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) r = container_of(resource, struct inbound_transaction_resource, resource); - if (is_fcp_request(r->request)) { - kfree(r->data); + if (r->is_fcp) { + fw_request_put(r->request); goto out; } if (a->length != fw_get_response_length(r->request)) { ret = -EINVAL; - kfree(r->request); + fw_request_put(r->request); goto out; } if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { ret = -EFAULT; - kfree(r->request); + fw_request_put(r->request); goto out; } fw_send_response(r->card, r->request, a->rcode); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index af498d767702..a9f70c96323e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -535,12 +535,6 @@ const struct fw_address_region fw_unit_space_region = { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; #endif /* 0 */ -static bool is_in_fcp_region(u64 offset, size_t length) -{ - return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && - offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END); -} - /** * fw_core_add_address_handler() - register for incoming requests * @handler: callback @@ -617,6 +611,7 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler) EXPORT_SYMBOL(fw_core_remove_address_handler); struct fw_request { + struct kref kref; struct fw_packet response; u32 request_header[4]; int ack; @@ -625,13 +620,33 @@ struct fw_request { u32 data[]; }; +void fw_request_get(struct fw_request *request) +{ + kref_get(&request->kref); +} + +static void release_request(struct kref *kref) +{ + struct fw_request *request = container_of(kref, struct fw_request, kref); + + kfree(request); +} + +void fw_request_put(struct fw_request *request) +{ + kref_put(&request->kref, release_request); +} + static void free_response_callback(struct fw_packet *packet, struct fw_card *card, int status) { - struct fw_request *request; + struct fw_request *request = container_of(packet, struct fw_request, response); - request = container_of(packet, struct fw_request, response); - kfree(request); + // Decrease the reference count since not at in-flight. + fw_request_put(request); + + // Decrease the reference count to release the object. + fw_request_put(request); } int fw_get_response_length(struct fw_request *r) @@ -782,6 +797,7 @@ static struct fw_request *allocate_request(struct fw_card *card, request = kmalloc(sizeof(*request) + length, GFP_ATOMIC); if (request == NULL) return NULL; + kref_init(&request->kref); request->response.speed = p->speed; request->response.timestamp = @@ -800,16 +816,22 @@ static struct fw_request *allocate_request(struct fw_card *card, return request; } +/** + * fw_send_response: - send response packet for asynchronous transaction. + * @card: interface to send the response at. + * @request: firewire request data for the transaction. + * @rcode: response code to send. + * + * Submit a response packet into the asynchronous response transmission queue. The @request + * is going to be released when the transmission successfully finishes later. + */ void fw_send_response(struct fw_card *card, struct fw_request *request, int rcode) { - if (WARN_ONCE(!request, "invalid for FCP address handlers")) - return; - /* unified transaction or broadcast transaction: don't respond */ if (request->ack != ACK_PENDING || HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { - kfree(request); + fw_request_put(request); return; } @@ -821,6 +843,9 @@ void fw_send_response(struct fw_card *card, fw_fill_response(&request->response, request->request_header, rcode, NULL, 0); + // Increase the reference count so that the object is kept during in-flight. + fw_request_get(request); + card->driver->send_response(card, &request->response); } EXPORT_SYMBOL(fw_send_response); @@ -910,7 +935,7 @@ static void handle_fcp_region_request(struct fw_card *card, rcu_read_lock(); list_for_each_entry_rcu(handler, &address_handler_list, link) { if (is_enclosing_handler(handler, offset, request->length)) - handler->address_callback(card, NULL, tcode, + handler->address_callback(card, request, tcode, destination, source, p->generation, offset, request->data, diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 71d5f16f311c..eafa4eaae737 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -244,6 +244,9 @@ int fw_get_response_length(struct fw_request *request); void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length); +void fw_request_get(struct fw_request *request); +void fw_request_put(struct fw_request *request); + #define FW_PHY_CONFIG_NO_NODE_ID -1 #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, @@ -254,4 +257,10 @@ static inline bool is_ping_packet(u32 *data) return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1]; } +static inline bool is_in_fcp_region(u64 offset, size_t length) +{ + return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && + offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END); +} + #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c index 9a2331eb1bfa..663acbb1854c 100644 --- a/drivers/mfd/wm97xx-core.c +++ b/drivers/mfd/wm97xx-core.c @@ -319,13 +319,11 @@ err_free_compat: return ret; } -static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +static void wm97xx_ac97_remove(struct ac97_codec_device *adev) { struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); snd_ac97_compat_release(wm97xx->ac97); - - return 0; } static const struct ac97_id wm97xx_ac97_ids[] = { diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index bd502368339e..55cace1fd77b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -469,7 +469,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, } /* Inform slave about the impending port prepare */ - sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP); + sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_PRE_PREP : SDW_OPS_PORT_PRE_DEPREP); /* Prepare Slave port implementing CP_SM */ if (!dpn_prop->simple_ch_prep_sm) { @@ -501,7 +501,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, } /* Inform slaves about ports prepared */ - sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP); + sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_POST_PREP : SDW_OPS_PORT_POST_DEPREP); /* Disable interrupt after Port de-prepare */ if (!prep && intr) diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 980019053e54..56505436d159 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -278,9 +278,8 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, * Otherwise there is a danger of recursion of inbound and outbound * transactions from and to the local node. * - * The callback is responsible that either fw_send_response() or kfree() - * is called on the @request, except for FCP registers for which the core - * takes care of that. + * The callback is responsible that fw_send_response() is called on the @request, except for FCP + * registers for which the core takes care of that. */ typedef void (*fw_address_callback_t)(struct fw_card *card, struct fw_request *request, diff --git a/include/linux/platform_data/asoc-ux500-msp.h b/include/linux/platform_data/asoc-ux500-msp.h deleted file mode 100644 index b8d0f730dda8..000000000000 --- a/include/linux/platform_data/asoc-ux500-msp.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson - */ - -#ifndef __MSP_H -#define __MSP_H - -#include <linux/platform_data/dma-ste-dma40.h> - -/* Platform data structure for a MSP I2S-device */ -struct msp_i2s_platform_data { - int id; - struct stedma40_chan_cfg *msp_i2s_dma_rx; - struct stedma40_chan_cfg *msp_i2s_dma_tx; -}; - -#endif diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 9e4537f409c2..7d9fbf9359e4 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -566,13 +566,15 @@ struct sdw_prepare_ch { * enum sdw_port_prep_ops: Prepare operations for Data Port * * @SDW_OPS_PORT_PRE_PREP: Pre prepare operation for the Port - * @SDW_OPS_PORT_PREP: Prepare operation for the Port + * @SDW_OPS_PORT_PRE_DEPREP: Pre deprepare operation for the Port * @SDW_OPS_PORT_POST_PREP: Post prepare operation for the Port + * @SDW_OPS_PORT_POST_DEPREP: Post deprepare operation for the Port */ enum sdw_port_prep_ops { SDW_OPS_PORT_PRE_PREP = 0, - SDW_OPS_PORT_PREP = 1, - SDW_OPS_PORT_POST_PREP = 2, + SDW_OPS_PORT_PRE_DEPREP, + SDW_OPS_PORT_POST_PREP, + SDW_OPS_PORT_POST_DEPREP, }; /** diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h index 9792d25fa369..2fc641cb1982 100644 --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -63,7 +63,7 @@ struct ac97_codec_device { struct ac97_codec_driver { struct device_driver driver; int (*probe)(struct ac97_codec_device *); - int (*remove)(struct ac97_codec_device *); + void (*remove)(struct ac97_codec_device *dev); void (*shutdown)(struct ac97_codec_device *); const struct ac97_id *id_table; }; diff --git a/include/sound/acp63_chip_offset_byte.h b/include/sound/acp63_chip_offset_byte.h index c9260e1640ae..d746c1cb0163 100644 --- a/include/sound/acp63_chip_offset_byte.h +++ b/include/sound/acp63_chip_offset_byte.h @@ -126,24 +126,24 @@ #define ACP_PAD_PULLDOWN_CTRL 0x0001448 #define ACP_PAD_DRIVE_STRENGTH_CTRL 0x000144C #define ACP_PAD_SCHMEN_CTRL 0x0001450 -#define ACP_SW_PAD_KEEPER_EN 0x0001454 -#define ACP_SW_WAKE_EN 0x0001458 +#define ACP_SW0_PAD_KEEPER_EN 0x0001454 +#define ACP_SW0_WAKE_EN 0x0001458 #define ACP_I2S_WAKE_EN 0x000145C #define ACP_SW1_WAKE_EN 0x0001460 -#define ACP_SW_I2S_ERROR_REASON 0x00018B4 -#define ACP_SW_POS_TRACK_I2S_TX_CTRL 0x00018B8 -#define ACP_SW_I2S_TX_DMA_POS 0x00018BC -#define ACP_SW_POS_TRACK_BT_TX_CTRL 0x00018C0 -#define ACP_SW_BT_TX_DMA_POS 0x00018C4 -#define ACP_SW_POS_TRACK_HS_TX_CTRL 0x00018C8 -#define ACP_SW_HS_TX_DMA_POS 0x00018CC -#define ACP_SW_POS_TRACK_I2S_RX_CTRL 0x00018D0 -#define ACP_SW_I2S_RX_DMA_POS 0x00018D4 -#define ACP_SW_POS_TRACK_BT_RX_CTRL 0x00018D8 -#define ACP_SW_BT_RX_DMA_POS 0x00018DC -#define ACP_SW_POS_TRACK_HS_RX_CTRL 0x00018E0 -#define ACP_SW_HS_RX_DMA_POS 0x00018E4 +#define ACP_SW0_I2S_ERROR_REASON 0x00018B4 +#define ACP_SW0_POS_TRACK_AUDIO0_TX_CTRL 0x00018B8 +#define ACP_SW0_AUDIO0_TX_DMA_POS 0x00018BC +#define ACP_SW0_POS_TRACK_AUDIO1_TX_CTRL 0x00018C0 +#define ACP_SW0_AUDIO1_TX_DMA_POS 0x00018C4 +#define ACP_SW0_POS_TRACK_AUDIO2_TX_CTRL 0x00018C8 +#define ACP_SW0_AUDIO2_TX_DMA_POS 0x00018CC +#define ACP_SW0_POS_TRACK_AUDIO0_RX_CTRL 0x00018D0 +#define ACP_SW0_AUDIO0_DMA_POS 0x00018D4 +#define ACP_SW0_POS_TRACK_AUDIO1_RX_CTRL 0x00018D8 +#define ACP_SW0_AUDIO1_RX_DMA_POS 0x00018DC +#define ACP_SW0_POS_TRACK_AUDIO2_RX_CTRL 0x00018E0 +#define ACP_SW0_AUDIO2_RX_DMA_POS 0x00018E4 #define ACP_ERROR_INTR_MASK1 0X0001974 #define ACP_ERROR_INTR_MASK2 0X0001978 #define ACP_ERROR_INTR_MASK3 0X000197C @@ -155,98 +155,80 @@ #define ACP_EXTERNAL_INTR_STAT 0x0001A0C #define ACP_EXTERNAL_INTR_STAT1 0x0001A10 #define ACP_ERROR_STATUS 0x0001A4C -#define ACP_P1_SW_I2S_ERROR_REASON 0x0001A50 -#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x0001A6C -#define ACP_P1_SW_I2S_TX_DMA_POS 0x0001A70 -#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x0001A74 -#define ACP_P1_SW_I2S_RX_DMA_POS 0x0001A78 +#define ACP_SW1_I2S_ERROR_REASON 0x0001A50 +#define ACP_SW1_POS_TRACK_AUDIO0_TX_CTRL 0x0001A6C +#define ACP_SW1_AUDIO0_TX_DMA_POS 0x0001A70 +#define ACP_SW1_POS_TRACK_AUDIO0_RX_CTRL 0x0001A74 +#define ACP_SW1_AUDIO0_RX_DMA_POS 0x0001A78 #define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x0001A7C #define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x0001A80 #define ACP_SCRATCH_REG_BASE_ADDR 0x0001A84 -#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x0001A88 -#define ACP_P1_SW_BT_TX_DMA_POS 0x0001A8C -#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x0001A90 -#define ACP_P1_SW_HS_TX_DMA_POS 0x0001A94 -#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x0001A98 -#define ACP_P1_SW_BT_RX_DMA_POS 0x0001A9C -#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x0001AA0 -#define ACP_P1_SW_HS_RX_DMA_POS 0x0001AA4 +#define ACP_SW1_POS_TRACK_AUDIO1_TX_CTRL 0x0001A88 +#define ACP_SW1_AUDIO1_TX_DMA_POS 0x0001A8C +#define ACP_SW1_POS_TRACK_AUDIO2_TX_CTRL 0x0001A90 +#define ACP_SW1_AUDIO2_TX_DMA_POS 0x0001A94 +#define ACP_SW1_POS_TRACK_AUDIO1_RX_CTRL 0x0001A98 +#define ACP_SW1_AUDIO1_RX_DMA_POS 0x0001A9C +#define ACP_SW1_POS_TRACK_AUDIO2_RX_CTRL 0x0001AA0 +#define ACP_SW1_AUDIO2_RX_DMA_POS 0x0001AA4 #define ACP_ERROR_INTR_MASK4 0X0001AEC #define ACP_ERROR_INTR_MASK5 0X0001AF0 /* Registers from ACP_AUDIO_BUFFERS block */ -#define ACP_I2S_RX_RINGBUFADDR 0x0002000 -#define ACP_I2S_RX_RINGBUFSIZE 0x0002004 -#define ACP_I2S_RX_LINKPOSITIONCNTR 0x0002008 -#define ACP_I2S_RX_FIFOADDR 0x000200C -#define ACP_I2S_RX_FIFOSIZE 0x0002010 -#define ACP_I2S_RX_DMA_SIZE 0x0002014 -#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x0002018 -#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x000201C -#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x0002020 -#define ACP_I2S_TX_RINGBUFADDR 0x0002024 -#define ACP_I2S_TX_RINGBUFSIZE 0x0002028 -#define ACP_I2S_TX_LINKPOSITIONCNTR 0x000202C -#define ACP_I2S_TX_FIFOADDR 0x0002030 -#define ACP_I2S_TX_FIFOSIZE 0x0002034 -#define ACP_I2S_TX_DMA_SIZE 0x0002038 -#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x000203C -#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x0002040 -#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x0002044 -#define ACP_BT_RX_RINGBUFADDR 0x0002048 -#define ACP_BT_RX_RINGBUFSIZE 0x000204C -#define ACP_BT_RX_LINKPOSITIONCNTR 0x0002050 -#define ACP_BT_RX_FIFOADDR 0x0002054 -#define ACP_BT_RX_FIFOSIZE 0x0002058 -#define ACP_BT_RX_DMA_SIZE 0x000205C -#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x0002060 -#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x0002064 -#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x0002068 -#define ACP_BT_TX_RINGBUFADDR 0x000206C -#define ACP_BT_TX_RINGBUFSIZE 0x0002070 -#define ACP_BT_TX_LINKPOSITIONCNTR 0x0002074 -#define ACP_BT_TX_FIFOADDR 0x0002078 -#define ACP_BT_TX_FIFOSIZE 0x000207C -#define ACP_BT_TX_DMA_SIZE 0x0002080 -#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x0002084 -#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x0002088 -#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x000208C -#define ACP_HS_RX_RINGBUFADDR 0x0002090 -#define ACP_HS_RX_RINGBUFSIZE 0x0002094 -#define ACP_HS_RX_LINKPOSITIONCNTR 0x0002098 -#define ACP_HS_RX_FIFOADDR 0x000209C -#define ACP_HS_RX_FIFOSIZE 0x00020A0 -#define ACP_HS_RX_DMA_SIZE 0x00020A4 -#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x00020A8 -#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x00020AC -#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x00020B0 -#define ACP_HS_TX_RINGBUFADDR 0x00020B4 -#define ACP_HS_TX_RINGBUFSIZE 0x00020B8 -#define ACP_HS_TX_LINKPOSITIONCNTR 0x00020BC -#define ACP_HS_TX_FIFOADDR 0x00020C0 -#define ACP_HS_TX_FIFOSIZE 0x00020C4 -#define ACP_HS_TX_DMA_SIZE 0x00020C8 -#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x00020CC -#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x00020D0 -#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x00020D4 -#define ACP_AUDIO_RX_RINGBUFADDR ACP_I2S_RX_RINGBUFADDR -#define ACP_AUDIO_RX_RINGBUFSIZE ACP_I2S_RX_RINGBUFSIZE -#define ACP_AUDIO_RX_LINKPOSITIONCNTR ACP_I2S_RX_LINKPOSITIONCNTR -#define ACP_AUDIO_RX_FIFOADDR ACP_I2S_RX_FIFOADDR -#define ACP_AUDIO_RX_FIFOSIZE ACP_I2S_RX_FIFOSIZE -#define ACP_AUDIO_RX_DMA_SIZE ACP_I2S_RX_DMA_SIZE -#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH -#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_I2S_RX_LINEARPOSITIONCNTR_LOW -#define ACP_AUDIO_RX_INTR_WATERMARK_SIZE ACP_I2S_RX_INTR_WATERMARK_SIZE -#define ACP_AUDIO_TX_RINGBUFADDR ACP_I2S_TX_RINGBUFADDR -#define ACP_AUDIO_TX_RINGBUFSIZE ACP_I2S_TX_RINGBUFSIZE -#define ACP_AUDIO_TX_LINKPOSITIONCNTR ACP_I2S_TX_LINKPOSITIONCNTR -#define ACP_AUDIO_TX_FIFOADDR ACP_I2S_TX_FIFOADDR -#define ACP_AUDIO_TX_FIFOSIZE ACP_I2S_TX_FIFOSIZE -#define ACP_AUDIO_TX_DMA_SIZE ACP_I2S_TX_DMA_SIZE -#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH -#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_I2S_TX_LINEARPOSITIONCNTR_LOW -#define ACP_AUDIO_TX_INTR_WATERMARK_SIZE ACP_I2S_TX_INTR_WATERMARK_SIZE +#define ACP_AUDIO0_RX_RINGBUFADDR 0x0002000 +#define ACP_AUDIO0_RX_RINGBUFSIZE 0x0002004 +#define ACP_AUDIO0_RX_LINKPOSITIONCNTR 0x0002008 +#define ACP_AUDIO0_RX_FIFOADDR 0x000200C +#define ACP_AUDIO0_RX_FIFOSIZE 0x0002010 +#define ACP_AUDIO0_RX_DMA_SIZE 0x0002014 +#define ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH 0x0002018 +#define ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW 0x000201C +#define ACP_AUDIO0_RX_INTR_WATERMARK_SIZE 0x0002020 +#define ACP_AUDIO0_TX_RINGBUFADDR 0x0002024 +#define ACP_AUDIO0_TX_RINGBUFSIZE 0x0002028 +#define ACP_AUDIO0_TX_LINKPOSITIONCNTR 0x000202C +#define ACP_AUDIO0_TX_FIFOADDR 0x0002030 +#define ACP_AUDIO0_TX_FIFOSIZE 0x0002034 +#define ACP_AUDIO0_TX_DMA_SIZE 0x0002038 +#define ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH 0x000203C +#define ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW 0x0002040 +#define ACP_AUDIO0_TX_INTR_WATERMARK_SIZE 0x0002044 +#define ACP_AUDIO1_RX_RINGBUFADDR 0x0002048 +#define ACP_AUDIO1_RX_RINGBUFSIZE 0x000204C +#define ACP_AUDIO1_RX_LINKPOSITIONCNTR 0x0002050 +#define ACP_AUDIO1_RX_FIFOADDR 0x0002054 +#define ACP_AUDIO1_RX_FIFOSIZE 0x0002058 +#define ACP_AUDIO1_RX_DMA_SIZE 0x000205C +#define ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH 0x0002060 +#define ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW 0x0002064 +#define ACP_AUDIO1_RX_INTR_WATERMARK_SIZE 0x0002068 +#define ACP_AUDIO1_TX_RINGBUFADDR 0x000206C +#define ACP_AUDIO1_TX_RINGBUFSIZE 0x0002070 +#define ACP_AUDIO1_TX_LINKPOSITIONCNTR 0x0002074 +#define ACP_AUDIO1_TX_FIFOADDR 0x0002078 +#define ACP_AUDIO1_TX_FIFOSIZE 0x000207C +#define ACP_AUDIO1_TX_DMA_SIZE 0x0002080 +#define ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH 0x0002084 +#define ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW 0x0002088 +#define ACP_AUDIO1_TX_INTR_WATERMARK_SIZE 0x000208C +#define ACP_AUDIO2_RX_RINGBUFADDR 0x0002090 +#define ACP_AUDIO2_RX_RINGBUFSIZE 0x0002094 +#define ACP_AUDIO2_RX_LINKPOSITIONCNTR 0x0002098 +#define ACP_AUDIO2_RX_FIFOADDR 0x000209C +#define ACP_AUDIO2_RX_FIFOSIZE 0x00020A0 +#define ACP_AUDIO2_RX_DMA_SIZE 0x00020A4 +#define ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH 0x00020A8 +#define ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW 0x00020AC +#define ACP_AUDIO2_RX_INTR_WATERMARK_SIZE 0x00020B0 +#define ACP_AUDIO2_TX_RINGBUFADDR 0x00020B4 +#define ACP_AUDIO2_TX_RINGBUFSIZE 0x00020B8 +#define ACP_AUDIO2_TX_LINKPOSITIONCNTR 0x00020BC +#define ACP_AUDIO2_TX_FIFOADDR 0x00020C0 +#define ACP_AUDIO2_TX_FIFOSIZE 0x00020C4 +#define ACP_AUDIO2_TX_DMA_SIZE 0x00020C8 +#define ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH 0x00020CC +#define ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW 0x00020D0 +#define ACP_AUDIO2_TX_INTR_WATERMARK_SIZE 0x00020D4 /* Registers from ACP_I2S_TDM block */ #define ACP_I2STDM_IER 0x0002400 @@ -292,367 +274,222 @@ #define ACP_WOV_ERROR_STATUS_REGISTER 0x0002C68 #define ACP_PDM_CLKDIV 0x0002C6C -/* Registers from ACP_SW_SWCLK block */ -#define ACP_SW_EN 0x0003000 -#define ACP_SW_EN_STATUS 0x0003004 -#define ACP_SW_FRAMESIZE 0x0003008 -#define ACP_SW_SSP_COUNTER 0x000300C -#define ACP_SW_AUDIO_TX_EN 0x0003010 -#define ACP_SW_AUDIO_TX_EN_STATUS 0x0003014 -#define ACP_SW_AUDIO_TX_FRAME_FORMAT 0x0003018 -#define ACP_SW_AUDIO_TX_SAMPLEINTERVAL 0x000301C -#define ACP_SW_AUDIO_TX_HCTRL_DP0 0x0003020 -#define ACP_SW_AUDIO_TX_HCTRL_DP1 0x0003024 -#define ACP_SW_AUDIO_TX_HCTRL_DP2 0x0003028 -#define ACP_SW_AUDIO_TX_HCTRL_DP3 0x000302C -#define ACP_SW_AUDIO_TX_OFFSET_DP0 0x0003030 -#define ACP_SW_AUDIO_TX_OFFSET_DP1 0x0003034 -#define ACP_SW_AUDIO_TX_OFFSET_DP2 0x0003038 -#define ACP_SW_AUDIO_TX_OFFSET_DP3 0x000303C -#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0 0x0003040 -#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP1 0x0003044 -#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP2 0x0003048 -#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP3 0x000304C -#define ACP_SW_BT_TX_EN 0x0003050 -#define ACP_SW_BT_TX_EN_STATUS 0x0003054 -#define ACP_SW_BT_TX_FRAME_FORMAT 0x0003058 -#define ACP_SW_BT_TX_SAMPLEINTERVAL 0x000305C -#define ACP_SW_BT_TX_HCTRL 0x0003060 -#define ACP_SW_BT_TX_OFFSET 0x0003064 -#define ACP_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003068 -#define ACP_SW_HEADSET_TX_EN 0x000306C -#define ACP_SW_HEADSET_TX_EN_STATUS 0x0003070 -#define ACP_SW_HEADSET_TX_FRAME_FORMAT 0x0003074 -#define ACP_SW_HEADSET_TX_SAMPLEINTERVAL 0x0003078 -#define ACP_SW_HEADSET_TX_HCTRL 0x000307C -#define ACP_SW_HEADSET_TX_OFFSET 0x0003080 -#define ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0 0x0003084 -#define ACP_SW_AUDIO_RX_EN 0x0003088 -#define ACP_SW_AUDIO_RX_EN_STATUS 0x000308C -#define ACP_SW_AUDIO_RX_FRAME_FORMAT 0x0003090 -#define ACP_SW_AUDIO_RX_SAMPLEINTERVAL 0x0003094 -#define ACP_SW_AUDIO_RX_HCTRL_DP0 0x0003098 -#define ACP_SW_AUDIO_RX_HCTRL_DP1 0x000309C -#define ACP_SW_AUDIO_RX_HCTRL_DP2 0x0003100 -#define ACP_SW_AUDIO_RX_HCTRL_DP3 0x0003104 -#define ACP_SW_AUDIO_RX_OFFSET_DP0 0x0003108 -#define ACP_SW_AUDIO_RX_OFFSET_DP1 0x000310C -#define ACP_SW_AUDIO_RX_OFFSET_DP2 0x0003110 -#define ACP_SW_AUDIO_RX_OFFSET_DP3 0x0003114 -#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0 0x0003118 -#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP1 0x000311C -#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP2 0x0003120 -#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP3 0x0003124 -#define ACP_SW_BT_RX_EN 0x0003128 -#define ACP_SW_BT_RX_EN_STATUS 0x000312C -#define ACP_SW_BT_RX_FRAME_FORMAT 0x0003130 -#define ACP_SW_BT_RX_SAMPLEINTERVAL 0x0003134 -#define ACP_SW_BT_RX_HCTRL 0x0003138 -#define ACP_SW_BT_RX_OFFSET 0x000313C -#define ACP_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003140 -#define ACP_SW_HEADSET_RX_EN 0x0003144 -#define ACP_SW_HEADSET_RX_EN_STATUS 0x0003148 -#define ACP_SW_HEADSET_RX_FRAME_FORMAT 0x000314C -#define ACP_SW_HEADSET_RX_SAMPLEINTERVAL 0x0003150 -#define ACP_SW_HEADSET_RX_HCTRL 0x0003154 -#define ACP_SW_HEADSET_RX_OFFSET 0x0003158 -#define ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0 0x000315C -#define ACP_SW_BPT_PORT_EN 0x0003160 -#define ACP_SW_BPT_PORT_EN_STATUS 0x0003164 -#define ACP_SW_BPT_PORT_FRAME_FORMAT 0x0003168 -#define ACP_SW_BPT_PORT_SAMPLEINTERVAL 0x000316C -#define ACP_SW_BPT_PORT_HCTRL 0x0003170 -#define ACP_SW_BPT_PORT_OFFSET 0x0003174 -#define ACP_SW_BPT_PORT_CHANNEL_ENABLE 0x0003178 -#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR 0x000317C -#define ACP_SW_CLK_RESUME_CTRL 0x0003180 -#define ACP_SW_CLK_RESUME_DELAY_CNTR 0x0003184 -#define ACP_SW_BUS_RESET_CTRL 0x0003188 -#define ACP_SW_PRBS_ERR_STATUS 0x000318C -#define SW_IMM_CMD_UPPER_WORD 0x0003230 -#define SW_IMM_CMD_LOWER_QWORD 0x0003234 -#define SW_IMM_RESP_UPPER_WORD 0x0003238 -#define SW_IMM_RESP_LOWER_QWORD 0x000323C -#define SW_IMM_CMD_STS 0x0003240 -#define SW_BRA_BASE_ADDRESS 0x0003244 -#define SW_BRA_TRANSFER_SIZE 0x0003248 -#define SW_BRA_DMA_BUSY 0x000324C -#define SW_BRA_RESP 0x0003250 -#define SW_BRA_RESP_FRAME_ADDR 0x0003254 -#define SW_BRA_CURRENT_TRANSFER_SIZE 0x0003258 -#define SW_STATE_CHANGE_STATUS_0TO7 0x000325C -#define SW_STATE_CHANGE_STATUS_8TO11 0x0003260 -#define SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003264 -#define SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003268 -#define SW_CLK_FREQUENCY_CTRL 0x000326C -#define SW_ERROR_INTR_MASK 0x0003270 -#define SW_PHY_TEST_MODE_DATA_OFF 0x0003274 +/* Registers from ACP_SW0_SWCLK block */ +#define ACP_SW0_EN 0x0003000 +#define ACP_SW0_EN_STATUS 0x0003004 +#define ACP_SW0_FRAMESIZE 0x0003008 +#define ACP_SW0_SSP_COUNTER 0x000300C +#define ACP_SW0_AUDIO0_TX_EN 0x0003010 +#define ACP_SW0_AUDIO0_TX_EN_STATUS 0x0003014 +#define ACP_SW0_AUDIO0_TX_FRAME_FORMAT 0x0003018 +#define ACP_SW0_AUDIO0_TX_SAMPLEINTERVAL 0x000301C +#define ACP_SW0_AUDIO0_TX_HCTRL_DP0 0x0003020 +#define ACP_SW0_AUDIO0_TX_HCTRL_DP1 0x0003024 +#define ACP_SW0_AUDIO0_TX_HCTRL_DP2 0x0003028 +#define ACP_SW0_AUDIO0_TX_HCTRL_DP3 0x000302C +#define ACP_SW0_AUDIO0_TX_OFFSET_DP0 0x0003030 +#define ACP_SW0_AUDIO0_TX_OFFSET_DP1 0x0003034 +#define ACP_SW0_AUDIO0_TX_OFFSET_DP2 0x0003038 +#define ACP_SW0_AUDIO0_TX_OFFSET_DP3 0x000303C +#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP0 0x0003040 +#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP1 0x0003044 +#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP2 0x0003048 +#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP3 0x000304C +#define ACP_SW0_AUDIO1_TX_EN 0x0003050 +#define ACP_SW0_AUDIO1_TX_EN_STATUS 0x0003054 +#define ACP_SW0_AUDIO1_TX_FRAME_FORMAT 0x0003058 +#define ACP_SW0_AUDIO1_TX_SAMPLEINTERVAL 0x000305C +#define ACP_SW0_AUDIO1_TX_HCTRL 0x0003060 +#define ACP_SW0_AUDIO1_TX_OFFSET 0x0003064 +#define ACP_SW0_AUDIO1_TX_CHANNEL_ENABLE_DP0 0x0003068 +#define ACP_SW0_AUDIO2_TX_EN 0x000306C +#define ACP_SW0_AUDIO2_TX_EN_STATUS 0x0003070 +#define ACP_SW0_AUDIO2_TX_FRAME_FORMAT 0x0003074 +#define ACP_SW0_AUDIO2_TX_SAMPLEINTERVAL 0x0003078 +#define ACP_SW0_AUDIO2_TX_HCTRL 0x000307C +#define ACP_SW0_AUDIO2_TX_OFFSET 0x0003080 +#define ACP_SW0_AUDIO2_TX_CHANNEL_ENABLE_DP0 0x0003084 +#define ACP_SW0_AUDIO0_RX_EN 0x0003088 +#define ACP_SW0_AUDIO0_RX_EN_STATUS 0x000308C +#define ACP_SW0_AUDIO0_RX_FRAME_FORMAT 0x0003090 +#define ACP_SW0_AUDIO0_RX_SAMPLEINTERVAL 0x0003094 +#define ACP_SW0_AUDIO0_RX_HCTRL_DP0 0x0003098 +#define ACP_SW0_AUDIO0_RX_HCTRL_DP1 0x000309C +#define ACP_SW0_AUDIO0_RX_HCTRL_DP2 0x0003100 +#define ACP_SW0_AUDIO0_RX_HCTRL_DP3 0x0003104 +#define ACP_SW0_AUDIO0_RX_OFFSET_DP0 0x0003108 +#define ACP_SW0_AUDIO0_RX_OFFSET_DP1 0x000310C +#define ACP_SW0_AUDIO0_RX_OFFSET_DP2 0x0003110 +#define ACP_SW0_AUDIO0_RX_OFFSET_DP3 0x0003114 +#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP0 0x0003118 +#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP1 0x000311C +#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP2 0x0003120 +#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP3 0x0003124 +#define ACP_SW0_AUDIO1_RX_EN 0x0003128 +#define ACP_SW0_AUDIO1_RX_EN_STATUS 0x000312C +#define ACP_SW0_AUDIO1_RX_FRAME_FORMAT 0x0003130 +#define ACP_SW0_AUDIO1_RX_SAMPLEINTERVAL 0x0003134 +#define ACP_SW0_AUDIO1_RX_HCTRL 0x0003138 +#define ACP_SW0_AUDIO1_RX_OFFSET 0x000313C +#define ACP_SW0_AUDIO1_RX_CHANNEL_ENABLE_DP0 0x0003140 +#define ACP_SW0_AUDIO2_RX_EN 0x0003144 +#define ACP_SW0_AUDIO2_RX_EN_STATUS 0x0003148 +#define ACP_SW0_AUDIO2_RX_FRAME_FORMAT 0x000314C +#define ACP_SW0_AUDIO2_RX_SAMPLEINTERVAL 0x0003150 +#define ACP_SW0_AUDIO2_RX_HCTRL 0x0003154 +#define ACP_SW0_AUDIO2_RX_OFFSET 0x0003158 +#define ACP_SW0_AUDIO2_RX_CHANNEL_ENABLE_DP0 0x000315C +#define ACP_SW0_BPT_PORT_EN 0x0003160 +#define ACP_SW0_BPT_PORT_EN_STATUS 0x0003164 +#define ACP_SW0_BPT_PORT_FRAME_FORMAT 0x0003168 +#define ACP_SW0_BPT_PORT_SAMPLEINTERVAL 0x000316C +#define ACP_SW0_BPT_PORT_HCTRL 0x0003170 +#define ACP_SW0_BPT_PORT_OFFSET 0x0003174 +#define ACP_SW0_BPT_PORT_CHANNEL_ENABLE 0x0003178 +#define ACP_SW0_BPT_PORT_FIRST_BYTE_ADDR 0x000317C +#define ACP_SW0_CLK_RESUME_CTRL 0x0003180 +#define ACP_SW0_CLK_RESUME_DELAY_CNTR 0x0003184 +#define ACP_SW0_BUS_RESET_CTRL 0x0003188 +#define ACP_SW0_PRBS_ERR_STATUS 0x000318C +#define ACP_SW0_IMM_CMD_UPPER_WORD 0x0003230 +#define ACP_SW0_IMM_CMD_LOWER_QWORD 0x0003234 +#define ACP_SW0_IMM_RESP_UPPER_WORD 0x0003238 +#define ACP_SW0_IMM_RESP_LOWER_QWORD 0x000323C +#define ACP_SW0_IMM_CMD_STS 0x0003240 +#define ACP_SW0_BRA_BASE_ADDRESS 0x0003244 +#define ACP_SW0_BRA_TRANSFER_SIZE 0x0003248 +#define ACP_SW0_BRA_DMA_BUSY 0x000324C +#define ACP_SW0_BRA_RESP 0x0003250 +#define ACP_SW0_BRA_RESP_FRAME_ADDR 0x0003254 +#define ACP_SW0_BRA_CURRENT_TRANSFER_SIZE 0x0003258 +#define ACP_SW0_STATECHANGE_STATUS_0TO7 0x000325C +#define ACP_SW0_STATECHANGE_STATUS_8TO11 0x0003260 +#define ACP_SW0_STATECHANGE_STATUS_MASK_0TO7 0x0003264 +#define ACP_SW0_STATECHANGE_STATUS_MASK_8TO11 0x0003268 +#define ACP_SW0_CLK_FREQUENCY_CTRL 0x000326C +#define ACP_SW0_ERROR_INTR_MASK 0x0003270 +#define ACP_SW0_PHY_TEST_MODE_DATA_OFF 0x0003274 /* Registers from ACP_P1_AUDIO_BUFFERS block */ -#define ACP_P1_I2S_RX_RINGBUFADDR 0x0003A00 -#define ACP_P1_I2S_RX_RINGBUFSIZE 0x0003A04 -#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x0003A08 -#define ACP_P1_I2S_RX_FIFOADDR 0x0003A0C -#define ACP_P1_I2S_RX_FIFOSIZE 0x0003A10 -#define ACP_P1_I2S_RX_DMA_SIZE 0x0003A14 -#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x0003A18 -#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x0003A1C -#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x0003A20 -#define ACP_P1_I2S_TX_RINGBUFADDR 0x0003A24 -#define ACP_P1_I2S_TX_RINGBUFSIZE 0x0003A28 -#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x0003A2C -#define ACP_P1_I2S_TX_FIFOADDR 0x0003A30 -#define ACP_P1_I2S_TX_FIFOSIZE 0x0003A34 -#define ACP_P1_I2S_TX_DMA_SIZE 0x0003A38 -#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x0003A3C -#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x0003A40 -#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x0003A44 -#define ACP_P1_BT_RX_RINGBUFADDR 0x0003A48 -#define ACP_P1_BT_RX_RINGBUFSIZE 0x0003A4C -#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x0003A50 -#define ACP_P1_BT_RX_FIFOADDR 0x0003A54 -#define ACP_P1_BT_RX_FIFOSIZE 0x0003A58 -#define ACP_P1_BT_RX_DMA_SIZE 0x0003A5C -#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x0003A60 -#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x0003A64 -#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x0003A68 -#define ACP_P1_BT_TX_RINGBUFADDR 0x0003A6C -#define ACP_P1_BT_TX_RINGBUFSIZE 0x0003A70 -#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x0003A74 -#define ACP_P1_BT_TX_FIFOADDR 0x0003A78 -#define ACP_P1_BT_TX_FIFOSIZE 0x0003A7C -#define ACP_P1_BT_TX_DMA_SIZE 0x0003A80 -#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x0003A84 -#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x0003A88 -#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x0003A8C -#define ACP_P1_HS_RX_RINGBUFADDR 0x0003A90 -#define ACP_P1_HS_RX_RINGBUFSIZE 0x0003A94 -#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x0003A98 -#define ACP_P1_HS_RX_FIFOADDR 0x0003A9C -#define ACP_P1_HS_RX_FIFOSIZE 0x0003AA0 -#define ACP_P1_HS_RX_DMA_SIZE 0x0003AA4 -#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x0003AA8 -#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x0003AAC -#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x0003AB0 -#define ACP_P1_HS_TX_RINGBUFADDR 0x0003AB4 -#define ACP_P1_HS_TX_RINGBUFSIZE 0x0003AB8 -#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x0003ABC -#define ACP_P1_HS_TX_FIFOADDR 0x0003AC0 -#define ACP_P1_HS_TX_FIFOSIZE 0x0003AC4 -#define ACP_P1_HS_TX_DMA_SIZE 0x0003AC8 -#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x0003ACC -#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x0003AD0 -#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x0003AD4 -#define ACP_P1_AUDIO_RX_RINGBUFADDR ACP_P1_I2S_RX_RINGBUFADDR -#define ACP_P1_AUDIO_RX_RINGBUFSIZE ACP_P1_I2S_RX_RINGBUFSIZE -#define ACP_P1_AUDIO_RX_LINKPOSITIONCNTR ACP_P1_I2S_RX_LINKPOSITIONCNTR -#define ACP_P1_AUDIO_RX_FIFOADDR ACP_P1_I2S_RX_FIFOADDR -#define ACP_P1_AUDIO_RX_FIFOSIZE ACP_P1_I2S_RX_FIFOSIZE -#define ACP_P1_AUDIO_RX_DMA_SIZE ACP_P1_I2S_RX_DMA_SIZE -#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH -#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW -#define ACP_P1_AUDIO_RX_INTR_WATERMARK_SIZE ACP_P1_I2S_RX_INTR_WATERMARK_SIZE -#define ACP_P1_AUDIO_TX_RINGBUFADDR ACP_P1_I2S_TX_RINGBUFADDR -#define ACP_P1_AUDIO_TX_RINGBUFSIZE ACP_P1_I2S_TX_RINGBUFSIZE -#define ACP_P1_AUDIO_TX_LINKPOSITIONCNTR ACP_P1_I2S_TX_LINKPOSITIONCNTR -#define ACP_P1_AUDIO_TX_FIFOADDR ACP_P1_I2S_TX_FIFOADDR -#define ACP_P1_AUDIO_TX_FIFOSIZE ACP_P1_I2S_TX_FIFOSIZE -#define ACP_P1_AUDIO_TX_DMA_SIZE ACP_P1_I2S_TX_DMA_SIZE -#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH -#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW -#define ACP_P1_AUDIO_TX_INTR_WATERMARK_SIZE ACP_P1_I2S_TX_INTR_WATERMARK_SIZE +#define ACP_P1_AUDIO0_RX_RINGBUFADDR 0x0003A00 +#define ACP_P1_AUDIO0_RX_RINGBUFSIZE 0x0003A04 +#define ACP_P1_AUDIO0_RX_LINKPOSITIONCNTR 0x0003A08 +#define ACP_P1_AUDIO0_RX_FIFOADDR 0x0003A0C +#define ACP_P1_AUDIO0_RX_FIFOSIZE 0x0003A10 +#define ACP_P1_AUDIO0_RX_DMA_SIZE 0x0003A14 +#define ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH 0x0003A18 +#define ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW 0x0003A1C +#define ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE 0x0003A20 +#define ACP_P1_AUDIO0_TX_RINGBUFADDR 0x0003A24 +#define ACP_P1_AUDIO0_TX_RINGBUFSIZE 0x0003A28 +#define ACP_P1_AUDIO0_TX_LINKPOSITIONCNTR 0x0003A2C +#define ACP_P1_AUDIO0_TX_FIFOADDR 0x0003A30 +#define ACP_P1_AUDIO0_TX_FIFOSIZE 0x0003A34 +#define ACP_P1_AUDIO0_TX_DMA_SIZE 0x0003A38 +#define ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH 0x0003A3C +#define ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW 0x0003A40 +#define ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE 0x0003A44 +#define ACP_P1_AUDIO1_RX_RINGBUFADDR 0x0003A48 +#define ACP_P1_AUDIO1_RX_RINGBUFSIZE 0x0003A4C +#define ACP_P1_AUDIO1_RX_LINKPOSITIONCNTR 0x0003A50 +#define ACP_P1_AUDIO1_RX_FIFOADDR 0x0003A54 +#define ACP_P1_AUDIO1_RX_FIFOSIZE 0x0003A58 +#define ACP_P1_AUDIO1_RX_DMA_SIZE 0x0003A5C +#define ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH 0x0003A60 +#define ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW 0x0003A64 +#define ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE 0x0003A68 +#define ACP_P1_AUDIO1_TX_RINGBUFADDR 0x0003A6C +#define ACP_P1_AUDIO1_TX_RINGBUFSIZE 0x0003A70 +#define ACP_P1_AUDIO1_TX_LINKPOSITIONCNTR 0x0003A74 +#define ACP_P1_AUDIO1_TX_FIFOADDR 0x0003A78 +#define ACP_P1_AUDIO1_TX_FIFOSIZE 0x0003A7C +#define ACP_P1_AUDIO1_TX_DMA_SIZE 0x0003A80 +#define ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH 0x0003A84 +#define ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW 0x0003A88 +#define ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE 0x0003A8C +#define ACP_P1_AUDIO2_RX_RINGBUFADDR 0x0003A90 +#define ACP_P1_AUDIO2_RX_RINGBUFSIZE 0x0003A94 +#define ACP_P1_AUDIO2_RX_LINKPOSITIONCNTR 0x0003A98 +#define ACP_P1_AUDIO2_RX_FIFOADDR 0x0003A9C +#define ACP_P1_AUDIO2_RX_FIFOSIZE 0x0003AA0 +#define ACP_P1_AUDIO2_RX_DMA_SIZE 0x0003AA4 +#define ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH 0x0003AA8 +#define ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW 0x0003AAC +#define ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE 0x0003AB0 +#define ACP_P1_AUDIO2_TX_RINGBUFADDR 0x0003AB4 +#define ACP_P1_AUDIO2_TX_RINGBUFSIZE 0x0003AB8 +#define ACP_P1_AUDIO2_TX_LINKPOSITIONCNTR 0x0003ABC +#define ACP_P1_AUDIO2_TX_FIFOADDR 0x0003AC0 +#define ACP_P1_AUDIO2_TX_FIFOSIZE 0x0003AC4 +#define ACP_P1_AUDIO2_TX_DMA_SIZE 0x0003AC8 +#define ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH 0x0003ACC +#define ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW 0x0003AD0 +#define ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE 0x0003AD4 -/* Registers from ACP_P1_SW_SWCLK block */ -#define ACP_P1_SW_EN 0x0003C00 -#define ACP_P1_SW_EN_STATUS 0x0003C04 -#define ACP_P1_SW_FRAMESIZE 0x0003C08 -#define ACP_P1_SW_SSP_COUNTER 0x0003C0C -#define ACP_P1_SW_BT_TX_EN 0x0003C50 -#define ACP_P1_SW_BT_TX_EN_STATUS 0x0003C54 -#define ACP_P1_SW_BT_TX_FRAME_FORMAT 0x0003C58 -#define ACP_P1_SW_BT_TX_SAMPLEINTERVAL 0x0003C5C -#define ACP_P1_SW_BT_TX_HCTRL 0x0003C60 -#define ACP_P1_SW_BT_TX_OFFSET 0x0003C64 -#define ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003C68 -#define ACP_P1_SW_BT_RX_EN 0x0003D28 -#define ACP_P1_SW_BT_RX_EN_STATUS 0x0003D2C -#define ACP_P1_SW_BT_RX_FRAME_FORMAT 0x0003D30 -#define ACP_P1_SW_BT_RX_SAMPLEINTERVAL 0x0003D34 -#define ACP_P1_SW_BT_RX_HCTRL 0x0003D38 -#define ACP_P1_SW_BT_RX_OFFSET 0x0003D3C -#define ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003D40 -#define ACP_P1_SW_BPT_PORT_EN 0x0003D60 -#define ACP_P1_SW_BPT_PORT_EN_STATUS 0x0003D64 -#define ACP_P1_SW_BPT_PORT_FRAME_FORMAT 0x0003D68 -#define ACP_P1_SW_BPT_PORT_SAMPLEINTERVAL 0x0003D6C -#define ACP_P1_SW_BPT_PORT_HCTRL 0x0003D70 -#define ACP_P1_SW_BPT_PORT_OFFSET 0x0003D74 -#define ACP_P1_SW_BPT_PORT_CHANNEL_ENABLE 0x0003D78 -#define ACP_P1_SW_BPT_PORT_FIRST_BYTE_ADDR 0x0003D7C -#define ACP_P1_SW_CLK_RESUME_CTRL 0x0003D80 -#define ACP_P1_SW_CLK_RESUME_DELAY_CNTR 0x0003D84 -#define ACP_P1_SW_BUS_RESET_CTRL 0x0003D88 -#define ACP_P1_SW_PRBS_ERR_STATUS 0x0003D8C +/* Registers from ACP_SW1_SWCLK block */ +#define ACP_SW1_EN 0x0003C00 +#define ACP_SW1_EN_STATUS 0x0003C04 +#define ACP_SW1_FRAMESIZE 0x0003C08 +#define ACP_SW1_SSP_COUNTER 0x0003C0C +#define ACP_SW1_AUDIO1_TX_EN 0x0003C50 +#define ACP_SW1_AUDIO1_TX_EN_STATUS 0x0003C54 +#define ACP_SW1_AUDIO1_TX_FRAME_FORMAT 0x0003C58 +#define ACP_SW1_AUDIO1_TX_SAMPLEINTERVAL 0x0003C5C +#define ACP_SW1_AUDIO1_TX_HCTRL 0x0003C60 +#define ACP_SW1_AUDIO1_TX_OFFSET 0x0003C64 +#define ACP_SW1_AUDIO1_TX_CHANNEL_ENABLE_DP0 0x0003C68 +#define ACP_SW1_AUDIO1_RX_EN 0x0003D28 +#define ACP_SW1_AUDIO1_RX_EN_STATUS 0x0003D2C +#define ACP_SW1_AUDIO1_RX_FRAME_FORMAT 0x0003D30 +#define ACP_SW1_AUDIO1_RX_SAMPLEINTERVAL 0x0003D34 +#define ACP_SW1_AUDIO1_RX_HCTRL 0x0003D38 +#define ACP_SW1_AUDIO1_RX_OFFSET 0x0003D3C +#define ACP_SW1_AUDIO1_RX_CHANNEL_ENABLE_DP0 0x0003D40 +#define ACP_SW1_BPT_PORT_EN 0x0003D60 +#define ACP_SW1_BPT_PORT_EN_STATUS 0x0003D64 +#define ACP_SW1_BPT_PORT_FRAME_FORMAT 0x0003D68 +#define ACP_SW1_BPT_PORT_SAMPLEINTERVAL 0x0003D6C +#define ACP_SW1_BPT_PORT_HCTRL 0x0003D70 +#define ACP_SW1_BPT_PORT_OFFSET 0x0003D74 +#define ACP_SW1_BPT_PORT_CHANNEL_ENABLE 0x0003D78 +#define ACP_SW1_BPT_PORT_FIRST_BYTE_ADDR 0x0003D7C +#define ACP_SW1_CLK_RESUME_CTRL 0x0003D80 +#define ACP_SW1_CLK_RESUME_DELAY_CNTR 0x0003D84 +#define ACP_SW1_BUS_RESET_CTRL 0x0003D88 +#define ACP_SW1_PRBS_ERR_STATUS 0x0003D8C -/* Registers from ACP_P1_SW_ACLK block */ -#define P1_SW_CORB_BASE_ADDRESS 0x0003E00 -#define P1_SW_CORB_WRITE_POINTER 0x0003E04 -#define P1_SW_CORB_READ_POINTER 0x0003E08 -#define P1_SW_CORB_CONTROL 0x0003E0C -#define P1_SW_CORB_SIZE 0x0003E14 -#define P1_SW_RIRB_BASE_ADDRESS 0x0003E18 -#define P1_SW_RIRB_WRITE_POINTER 0x0003E1C -#define P1_SW_RIRB_RESPONSE_INTERRUPT_COUNT 0x0003E20 -#define P1_SW_RIRB_CONTROL 0x0003E24 -#define P1_SW_RIRB_SIZE 0x0003E28 -#define P1_SW_RIRB_FIFO_MIN_THDL 0x0003E2C -#define P1_SW_IMM_CMD_UPPER_WORD 0x0003E30 -#define P1_SW_IMM_CMD_LOWER_QWORD 0x0003E34 -#define P1_SW_IMM_RESP_UPPER_WORD 0x0003E38 -#define P1_SW_IMM_RESP_LOWER_QWORD 0x0003E3C -#define P1_SW_IMM_CMD_STS 0x0003E40 -#define P1_SW_BRA_BASE_ADDRESS 0x0003E44 -#define P1_SW_BRA_TRANSFER_SIZE 0x0003E48 -#define P1_SW_BRA_DMA_BUSY 0x0003E4C -#define P1_SW_BRA_RESP 0x0003E50 -#define P1_SW_BRA_RESP_FRAME_ADDR 0x0003E54 -#define P1_SW_BRA_CURRENT_TRANSFER_SIZE 0x0003E58 -#define P1_SW_STATE_CHANGE_STATUS_0TO7 0x0003E5C -#define P1_SW_STATE_CHANGE_STATUS_8TO11 0x0003E60 -#define P1_SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003E64 -#define P1_SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003E68 -#define P1_SW_CLK_FREQUENCY_CTRL 0x0003E6C -#define P1_SW_ERROR_INTR_MASK 0x0003E70 -#define P1_SW_PHY_TEST_MODE_DATA_OFF 0x0003E74 +/* Registers from ACP_SW1_ACLK block */ +#define ACP_SW1_CORB_BASE_ADDRESS 0x0003E00 +#define ACP_SW1_CORB_WRITE_POINTER 0x0003E04 +#define ACP_SW1_CORB_READ_POINTER 0x0003E08 +#define ACP_SW1_CORB_CONTROL 0x0003E0C +#define ACP_SW1_CORB_SIZE 0x0003E14 +#define ACP_SW1_RIRB_BASE_ADDRESS 0x0003E18 +#define ACP_SW1_RIRB_WRITE_POINTER 0x0003E1C +#define ACP_SW1_RIRB_RESPONSE_INTERRUPT_COUNT 0x0003E20 +#define ACP_SW1_RIRB_CONTROL 0x0003E24 +#define ACP_SW1_RIRB_SIZE 0x0003E28 +#define ACP_SW1_RIRB_FIFO_MIN_THDL 0x0003E2C +#define ACP_SW1_IMM_CMD_UPPER_WORD 0x0003E30 +#define ACP_SW1_IMM_CMD_LOWER_QWORD 0x0003E34 +#define ACP_SW1_IMM_RESP_UPPER_WORD 0x0003E38 +#define ACP_SW1_IMM_RESP_LOWER_QWORD 0x0003E3C +#define ACP_SW1_IMM_CMD_STS 0x0003E40 +#define ACP_SW1_BRA_BASE_ADDRESS 0x0003E44 +#define ACP_SW1_BRA_TRANSFER_SIZE 0x0003E48 +#define ACP_SW1_BRA_DMA_BUSY 0x0003E4C +#define ACP_SW1_BRA_RESP 0x0003E50 +#define ACP_SW1_BRA_RESP_FRAME_ADDR 0x0003E54 +#define ACP_SW1_BRA_CURRENT_TRANSFER_SIZE 0x0003E58 +#define ACP_SW1_STATECHANGE_STATUS_0TO7 0x0003E5C +#define ACP_SW1_STATECHANGE_STATUS_8TO11 0x0003E60 +#define ACP_SW1_STATECHANGE_STATUS_MASK_0TO7 0x0003E64 +#define ACP_SW1_STATECHANGE_STATUS_MASK_8TO11 0x0003E68 +#define ACP_SW1_CLK_FREQUENCY_CTRL 0x0003E6C +#define ACP_SW1_ERROR_INTR_MASK 0x0003E70 +#define ACP_SW1_PHY_TEST_MODE_DATA_OFF 0x0003E74 /* Registers from ACP_SCRATCH block */ -#define ACP_SCRATCH_REG_0 0x0010000 -#define ACP_SCRATCH_REG_1 0x0010004 -#define ACP_SCRATCH_REG_2 0x0010008 -#define ACP_SCRATCH_REG_3 0x001000C -#define ACP_SCRATCH_REG_4 0x0010010 -#define ACP_SCRATCH_REG_5 0x0010014 -#define ACP_SCRATCH_REG_6 0x0010018 -#define ACP_SCRATCH_REG_7 0x001001C -#define ACP_SCRATCH_REG_8 0x0010020 -#define ACP_SCRATCH_REG_9 0x0010024 -#define ACP_SCRATCH_REG_10 0x0010028 -#define ACP_SCRATCH_REG_11 0x001002C -#define ACP_SCRATCH_REG_12 0x0010030 -#define ACP_SCRATCH_REG_13 0x0010034 -#define ACP_SCRATCH_REG_14 0x0010038 -#define ACP_SCRATCH_REG_15 0x001003C -#define ACP_SCRATCH_REG_16 0x0010040 -#define ACP_SCRATCH_REG_17 0x0010044 -#define ACP_SCRATCH_REG_18 0x0010048 -#define ACP_SCRATCH_REG_19 0x001004C -#define ACP_SCRATCH_REG_20 0x0010050 -#define ACP_SCRATCH_REG_21 0x0010054 -#define ACP_SCRATCH_REG_22 0x0010058 -#define ACP_SCRATCH_REG_23 0x001005C -#define ACP_SCRATCH_REG_24 0x0010060 -#define ACP_SCRATCH_REG_25 0x0010064 -#define ACP_SCRATCH_REG_26 0x0010068 -#define ACP_SCRATCH_REG_27 0x001006C -#define ACP_SCRATCH_REG_28 0x0010070 -#define ACP_SCRATCH_REG_29 0x0010074 -#define ACP_SCRATCH_REG_30 0x0010078 -#define ACP_SCRATCH_REG_31 0x001007C -#define ACP_SCRATCH_REG_32 0x0010080 -#define ACP_SCRATCH_REG_33 0x0010084 -#define ACP_SCRATCH_REG_34 0x0010088 -#define ACP_SCRATCH_REG_35 0x001008C -#define ACP_SCRATCH_REG_36 0x0010090 -#define ACP_SCRATCH_REG_37 0x0010094 -#define ACP_SCRATCH_REG_38 0x0010098 -#define ACP_SCRATCH_REG_39 0x001009C -#define ACP_SCRATCH_REG_40 0x00100A0 -#define ACP_SCRATCH_REG_41 0x00100A4 -#define ACP_SCRATCH_REG_42 0x00100A8 -#define ACP_SCRATCH_REG_43 0x00100AC -#define ACP_SCRATCH_REG_44 0x00100B0 -#define ACP_SCRATCH_REG_45 0x00100B4 -#define ACP_SCRATCH_REG_46 0x00100B8 -#define ACP_SCRATCH_REG_47 0x00100BC -#define ACP_SCRATCH_REG_48 0x00100C0 -#define ACP_SCRATCH_REG_49 0x00100C4 -#define ACP_SCRATCH_REG_50 0x00100C8 -#define ACP_SCRATCH_REG_51 0x00100CC -#define ACP_SCRATCH_REG_52 0x00100D0 -#define ACP_SCRATCH_REG_53 0x00100D4 -#define ACP_SCRATCH_REG_54 0x00100D8 -#define ACP_SCRATCH_REG_55 0x00100DC -#define ACP_SCRATCH_REG_56 0x00100E0 -#define ACP_SCRATCH_REG_57 0x00100E4 -#define ACP_SCRATCH_REG_58 0x00100E8 -#define ACP_SCRATCH_REG_59 0x00100EC -#define ACP_SCRATCH_REG_60 0x00100F0 -#define ACP_SCRATCH_REG_61 0x00100F4 -#define ACP_SCRATCH_REG_62 0x00100F8 -#define ACP_SCRATCH_REG_63 0x00100FC -#define ACP_SCRATCH_REG_64 0x0010100 -#define ACP_SCRATCH_REG_65 0x0010104 -#define ACP_SCRATCH_REG_66 0x0010108 -#define ACP_SCRATCH_REG_67 0x001010C -#define ACP_SCRATCH_REG_68 0x0010110 -#define ACP_SCRATCH_REG_69 0x0010114 -#define ACP_SCRATCH_REG_70 0x0010118 -#define ACP_SCRATCH_REG_71 0x001011C -#define ACP_SCRATCH_REG_72 0x0010120 -#define ACP_SCRATCH_REG_73 0x0010124 -#define ACP_SCRATCH_REG_74 0x0010128 -#define ACP_SCRATCH_REG_75 0x001012C -#define ACP_SCRATCH_REG_76 0x0010130 -#define ACP_SCRATCH_REG_77 0x0010134 -#define ACP_SCRATCH_REG_78 0x0010138 -#define ACP_SCRATCH_REG_79 0x001013C -#define ACP_SCRATCH_REG_80 0x0010140 -#define ACP_SCRATCH_REG_81 0x0010144 -#define ACP_SCRATCH_REG_82 0x0010148 -#define ACP_SCRATCH_REG_83 0x001014C -#define ACP_SCRATCH_REG_84 0x0010150 -#define ACP_SCRATCH_REG_85 0x0010154 -#define ACP_SCRATCH_REG_86 0x0010158 -#define ACP_SCRATCH_REG_87 0x001015C -#define ACP_SCRATCH_REG_88 0x0010160 -#define ACP_SCRATCH_REG_89 0x0010164 -#define ACP_SCRATCH_REG_90 0x0010168 -#define ACP_SCRATCH_REG_91 0x001016C -#define ACP_SCRATCH_REG_92 0x0010170 -#define ACP_SCRATCH_REG_93 0x0010174 -#define ACP_SCRATCH_REG_94 0x0010178 -#define ACP_SCRATCH_REG_95 0x001017C -#define ACP_SCRATCH_REG_96 0x0010180 -#define ACP_SCRATCH_REG_97 0x0010184 -#define ACP_SCRATCH_REG_98 0x0010188 -#define ACP_SCRATCH_REG_99 0x001018C -#define ACP_SCRATCH_REG_100 0x0010190 -#define ACP_SCRATCH_REG_101 0x0010194 -#define ACP_SCRATCH_REG_102 0x0010198 -#define ACP_SCRATCH_REG_103 0x001019C -#define ACP_SCRATCH_REG_104 0x00101A0 -#define ACP_SCRATCH_REG_105 0x00101A4 -#define ACP_SCRATCH_REG_106 0x00101A8 -#define ACP_SCRATCH_REG_107 0x00101AC -#define ACP_SCRATCH_REG_108 0x00101B0 -#define ACP_SCRATCH_REG_109 0x00101B4 -#define ACP_SCRATCH_REG_110 0x00101B8 -#define ACP_SCRATCH_REG_111 0x00101BC -#define ACP_SCRATCH_REG_112 0x00101C0 -#define ACP_SCRATCH_REG_113 0x00101C4 -#define ACP_SCRATCH_REG_114 0x00101C8 -#define ACP_SCRATCH_REG_115 0x00101CC -#define ACP_SCRATCH_REG_116 0x00101D0 -#define ACP_SCRATCH_REG_117 0x00101D4 -#define ACP_SCRATCH_REG_118 0x00101D8 -#define ACP_SCRATCH_REG_119 0x00101DC -#define ACP_SCRATCH_REG_120 0x00101E0 -#define ACP_SCRATCH_REG_121 0x00101E4 -#define ACP_SCRATCH_REG_122 0x00101E8 -#define ACP_SCRATCH_REG_123 0x00101EC -#define ACP_SCRATCH_REG_124 0x00101F0 -#define ACP_SCRATCH_REG_125 0x00101F4 -#define ACP_SCRATCH_REG_126 0x00101F8 -#define ACP_SCRATCH_REG_127 0x00101FC -#define ACP_SCRATCH_REG_128 0x0010200 +#define ACP_SCRATCH_REG_0 0x0010000 + #endif diff --git a/include/sound/core.h b/include/sound/core.h index 4365c35d038b..3edc4ab08774 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -286,10 +286,10 @@ int snd_devm_card_new(struct device *parent, int idx, const char *xid, struct module *module, size_t extra_size, struct snd_card **card_ret); -int snd_card_disconnect(struct snd_card *card); +void snd_card_disconnect(struct snd_card *card); void snd_card_disconnect_sync(struct snd_card *card); -int snd_card_free(struct snd_card *card); -int snd_card_free_when_closed(struct snd_card *card); +void snd_card_free(struct snd_card *card); +void snd_card_free_when_closed(struct snd_card *card); int snd_card_free_on_error(struct device *dev, int ret); void snd_card_set_id(struct snd_card *card, const char *id); int snd_card_register(struct snd_card *card); diff --git a/include/sound/cs42l42.h b/include/sound/cs42l42.h index 1d1c24fdd0ca..3994e933db19 100644 --- a/include/sound/cs42l42.h +++ b/include/sound/cs42l42.h @@ -34,6 +34,7 @@ #define CS42L42_PAGE_24 0x2400 #define CS42L42_PAGE_25 0x2500 #define CS42L42_PAGE_26 0x2600 +#define CS42L42_PAGE_27 0x2700 #define CS42L42_PAGE_28 0x2800 #define CS42L42_PAGE_29 0x2900 #define CS42L42_PAGE_2A 0x2A00 @@ -720,6 +721,10 @@ #define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09) +/* Page 0x27 DMA */ +#define CS42L42_SOFT_RESET_REBOOT (CS42L42_PAGE_27 + 0x01) +#define CS42L42_SFT_RST_REBOOT_MASK BIT(1) + /* Page 0x28 S/PDIF Registers */ #define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01) #define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index eba23daf2c29..bbb7805e85d8 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -259,6 +259,7 @@ struct hda_codec { unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ + unsigned int ctl_dev_id:1; /* old control element id build behaviour */ #ifdef CONFIG_PM unsigned long power_on_acct; diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index a6872537724d..536612c6ab0c 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -575,7 +575,7 @@ void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev); int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev); int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int format_val); -void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start); +void snd_hdac_stream_start(struct hdac_stream *azx_dev); void snd_hdac_stream_stop(struct hdac_stream *azx_dev); void snd_hdac_stop_streams(struct hdac_bus *bus); void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus); diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 38590f1ae9ee..a3f3f3aa9e6e 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -69,6 +69,7 @@ struct asoc_simple_priv { } *dai_props; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; + struct snd_soc_jack *aux_jacks; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; @@ -187,6 +188,8 @@ int asoc_simple_parse_pin_switches(struct snd_soc_card *card, int asoc_simple_init_jack(struct snd_soc_card *card, struct asoc_simple_jack *sjack, int is_hp, char *prefix, char *pin); +int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, + char *prefix); int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct link_info *li); int asoc_simple_remove(struct platform_device *pdev); diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index c26ffb033777..3203d35bc8c1 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -98,6 +98,7 @@ struct snd_soc_component_driver { int source, unsigned int freq_in, unsigned int freq_out); int (*set_jack)(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data); + int (*get_jack_type)(struct snd_soc_component *component); /* DT */ int (*of_xlate_dai_name)(struct snd_soc_component *component, @@ -384,6 +385,7 @@ int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, unsigned int freq_out); int snd_soc_component_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data); +int snd_soc_component_get_jack_type(struct snd_soc_component *component); void snd_soc_component_seq_notifier(struct snd_soc_component *component, enum snd_soc_dapm_type type, int subseq); diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ea7509672086..e3906ecda740 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -423,6 +423,16 @@ struct snd_soc_dai_driver { int remove_order; }; +/* for Playback/Capture */ +struct snd_soc_dai_stream { + struct snd_soc_dapm_widget *widget; + + unsigned int active; /* usage count */ + unsigned int tdm_mask; /* CODEC TDM slot masks and params (for fixup) */ + + void *dma_data; /* DAI DMA data */ +}; + /* * Digital Audio Interface runtime data. * @@ -437,14 +447,7 @@ struct snd_soc_dai { struct snd_soc_dai_driver *driver; /* DAI runtime info */ - unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */ - - struct snd_soc_dapm_widget *playback_widget; - struct snd_soc_dapm_widget *capture_widget; - - /* DAI DMA data */ - void *playback_dma_data; - void *capture_dma_data; + struct snd_soc_dai_stream stream[SNDRV_PCM_STREAM_LAST + 1]; /* Symmetry data - only valid if symmetry is being enforced */ unsigned int rate; @@ -454,10 +457,6 @@ struct snd_soc_dai { /* parent platform/codec */ struct snd_soc_component *component; - /* CODEC TDM slot masks and params (for fixup) */ - unsigned int tx_mask; - unsigned int rx_mask; - struct list_head list; /* function mark */ @@ -477,36 +476,59 @@ snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) &dai->driver->playback : &dai->driver->capture; } +#define snd_soc_dai_get_widget_playback(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK) +#define snd_soc_dai_get_widget_capture(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE) static inline -struct snd_soc_dapm_widget *snd_soc_dai_get_widget( - struct snd_soc_dai *dai, int stream) +struct snd_soc_dapm_widget *snd_soc_dai_get_widget(struct snd_soc_dai *dai, int stream) { - return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dai->playback_widget : dai->capture_widget; + return dai->stream[stream].widget; } -static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, - const struct snd_pcm_substream *ss) +#define snd_soc_dai_set_widget_playback(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_PLAYBACK, widget) +#define snd_soc_dai_set_widget_capture(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_CAPTURE, widget) +static inline +void snd_soc_dai_set_widget(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget) { - return (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dai->playback_dma_data : dai->capture_dma_data; + dai->stream[stream].widget = widget; } -static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai, - const struct snd_pcm_substream *ss, - void *data) +#define snd_soc_dai_dma_data_get_playback(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK) +#define snd_soc_dai_dma_data_get_capture(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE) +#define snd_soc_dai_get_dma_data(dai, ss) snd_soc_dai_dma_data_get(dai, ss->stream) +static inline void *snd_soc_dai_dma_data_get(const struct snd_soc_dai *dai, int stream) { - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = data; - else - dai->capture_dma_data = data; + return dai->stream[stream].dma_data; +} + +#define snd_soc_dai_dma_data_set_playback(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data) +#define snd_soc_dai_dma_data_set_capture(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data) +#define snd_soc_dai_set_dma_data(dai, ss, data) snd_soc_dai_dma_data_set(dai, ss->stream, data) +static inline void snd_soc_dai_dma_data_set(struct snd_soc_dai *dai, int stream, void *data) +{ + dai->stream[stream].dma_data = data; } -static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, - void *playback, void *capture) +static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *playback, void *capture) { - dai->playback_dma_data = playback; - dai->capture_dma_data = capture; + snd_soc_dai_dma_data_set_playback(dai, playback); + snd_soc_dai_dma_data_set_capture(dai, capture); +} + +static inline unsigned int snd_soc_dai_tdm_mask_get(struct snd_soc_dai *dai, int stream) +{ + return dai->stream[stream].tdm_mask; +} + +static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream, + unsigned int tdm_mask) +{ + dai->stream[stream].tdm_mask = tdm_mask; +} + +static inline unsigned int snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) +{ + /* see snd_soc_dai_action() for setup */ + return dai->stream[stream].active; } static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai, @@ -561,10 +583,4 @@ static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, return ERR_PTR(-ENOTSUPP); } -static inline unsigned int -snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) -{ - return dai->stream_active[stream]; -} - #endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 77495e5988c1..64915ebd641e 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -16,6 +16,7 @@ #include <sound/asoc.h> struct device; +struct snd_pcm_substream; struct snd_soc_pcm_runtime; struct soc_enum; diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 2864aed72998..1e7d09556fe3 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -162,6 +162,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir); +int widget_in_list(struct snd_soc_dapm_widget_list *list, + struct snd_soc_dapm_widget *widget); #define dpcm_be_dai_startup_rollback(fe, stream, last) \ dpcm_be_dai_stop(fe, stream, 0, last) diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index b4b896f83b94..f055c6917f6c 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -62,7 +62,7 @@ struct snd_soc_dobj { enum snd_soc_dobj_type type; unsigned int index; /* objects can belong in different groups */ struct list_head list; - struct snd_soc_tplg_ops *ops; + int (*unload)(struct snd_soc_component *comp, struct snd_soc_dobj *dobj); union { struct snd_soc_dobj_control control; struct snd_soc_dobj_widget widget; diff --git a/include/sound/soc.h b/include/sound/soc.h index 37bbfc8b45cb..e58b43b5da7c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1052,6 +1052,12 @@ struct snd_soc_card { #define for_each_card_widgets_safe(card, w, _w) \ list_for_each_entry_safe(w, _w, &card->widgets, list) + +static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card) +{ + return card && card->instantiated; +} + /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { struct device *dev; diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 622193be7ac4..d31349bf011d 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0) #define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) +/* pipeline set state IPC msg extension */ +#define SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI BIT(0) + /* load library ipc msg */ #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT) diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 3532ac7046d7..1e86872c151f 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -14,6 +14,7 @@ #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479 #define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL 0x7473636d #define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE 0x4d545244 +#define SNDRV_FIREWIRE_EVENT_FF400_MESSAGE 0x4f6c6761 struct snd_firewire_event_common { unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ @@ -72,6 +73,30 @@ struct snd_firewire_event_motu_register_dsp_change { __u32 changes[]; /* Encoded event for change of register DSP. */ }; +/** + * struct snd_firewire_event_ff400_message - the container for message from Fireface 400 when + * operating hardware knob. + * + * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE. + * @message_count: The number of messages. + * @messages.message: The messages expressing hardware knob operation. + * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous + * transaction was sent to deliver the message. It has 16 bit unsigned integer + * value. The higher 3 bits of value expresses the lower three bits of second + * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle + * field up to 7999. + * + * The structure expresses message transmitted by Fireface 400 when operating hardware knob. + */ +struct snd_firewire_event_ff400_message { + unsigned int type; + unsigned int message_count; + struct { + __u32 message; + __u32 tstamp; + } messages[]; +}; + union snd_firewire_event { struct snd_firewire_event_common common; struct snd_firewire_event_lock_status lock_status; @@ -81,6 +106,7 @@ union snd_firewire_event { struct snd_firewire_event_tascam_control tascam_control; struct snd_firewire_event_motu_notification motu_notification; struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change; + struct snd_firewire_event_ff400_message ff400_message; }; diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h index 754f02b2f444..4ffe546aa409 100644 --- a/include/uapi/sound/intel/avs/tokens.h +++ b/include/uapi/sound/intel/avs/tokens.h @@ -108,6 +108,7 @@ enum avs_tplg_token { AVS_TKN_MOD_CORE_ID_U8 = 1704, AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705, AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706, + AVS_TKN_MOD_KCONTROL_ID_U32 = 1707, /* struct avs_tplg_path_template */ AVS_TKN_PATH_TMPL_ID_U32 = 1801, @@ -121,6 +122,9 @@ enum avs_tplg_token { AVS_TKN_PIN_FMT_INDEX_U32 = 2201, AVS_TKN_PIN_FMT_IOBS_U32 = 2202, AVS_TKN_PIN_FMT_AFMT_ID_U32 = 2203, + + /* struct avs_tplg_kcontrol */ + AVS_TKN_KCONTROL_ID_U32 = 2301, }; #endif diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 045330883a96..6067c04ce4c0 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -524,10 +524,9 @@ static void ac97_bus_remove(struct device *dev) if (ret < 0) return; - ret = adrv->remove(adev); + adrv->remove(adev); pm_runtime_put_noidle(dev); - if (ret == 0) - ac97_put_disable_clk(adev); + ac97_put_disable_clk(adev); pm_runtime_disable(dev); } diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index ec4ef18555bc..850dc8c53e9b 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1094,7 +1094,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) return -ENODEV; } -static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) +static void aoa_fabric_layout_remove(struct soundbus_dev *sdev) { struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); int i; @@ -1123,7 +1123,6 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) kfree(ldev); sdev->pcmid = -1; sdev->pcmname = NULL; - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index 3a99c1f1a3ca..db40f9d042b4 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -185,7 +185,7 @@ struct soundbus_driver { /* we don't implement any matching at all */ int (*probe)(struct soundbus_dev* dev); - int (*remove)(struct soundbus_dev* dev); + void (*remove)(struct soundbus_dev *dev); int (*shutdown)(struct soundbus_dev* dev); diff --git a/sound/core/init.c b/sound/core/init.c index 5377f94eb211..df0c22480375 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -489,17 +489,17 @@ static const struct file_operations snd_shutdown_f_ops = * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). */ -int snd_card_disconnect(struct snd_card *card) +void snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; if (!card) - return -EINVAL; + return; spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); - return 0; + return; } card->shutdown = 1; @@ -548,7 +548,6 @@ int snd_card_disconnect(struct snd_card *card) wake_up(&card->power_sleep); snd_power_sync_ref(card); #endif - return 0; } EXPORT_SYMBOL(snd_card_disconnect); @@ -563,15 +562,7 @@ EXPORT_SYMBOL(snd_card_disconnect); */ void snd_card_disconnect_sync(struct snd_card *card) { - int err; - - err = snd_card_disconnect(card); - if (err < 0) { - dev_err(card->dev, - "snd_card_disconnect error (%d), skipping sync\n", - err); - return; - } + snd_card_disconnect(card); spin_lock_irq(&card->files_lock); wait_event_lock_irq(card->remove_sleep, @@ -617,13 +608,14 @@ static int snd_card_do_free(struct snd_card *card) * * Return: zero if successful, or a negative error code */ -int snd_card_free_when_closed(struct snd_card *card) +void snd_card_free_when_closed(struct snd_card *card) { - int ret = snd_card_disconnect(card); - if (ret) - return ret; + if (!card) + return; + + snd_card_disconnect(card); put_device(&card->card_dev); - return 0; + return; } EXPORT_SYMBOL(snd_card_free_when_closed); @@ -640,10 +632,9 @@ EXPORT_SYMBOL(snd_card_free_when_closed); * Return: Zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ -int snd_card_free(struct snd_card *card) +void snd_card_free(struct snd_card *card) { DECLARE_COMPLETION_ONSTACK(released); - int ret; /* The call of snd_card_free() is allowed from various code paths; * a manual call from the driver and the call via devres_free, and @@ -652,16 +643,13 @@ int snd_card_free(struct snd_card *card) * the check here at the beginning. */ if (card->releasing) - return 0; + return; card->release_completion = &released; - ret = snd_card_free_when_closed(card); - if (ret) - return ret; + snd_card_free_when_closed(card); + /* wait, until all devices are ready for the free operation */ wait_for_completion(&released); - - return 0; } EXPORT_SYMBOL(snd_card_free); diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index d9c700f652bb..3660c312bf33 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -36,8 +36,6 @@ struct amdtp_am824 { u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; u8 midi_position; - - unsigned int frame_multiplier; }; /** @@ -59,8 +57,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, { struct amdtp_am824 *p = s->protocol; unsigned int midi_channels; - unsigned int i; - int err; + unsigned int pcm_frame_multiplier; + int i, err; if (amdtp_stream_running(s)) return -EINVAL; @@ -77,8 +75,18 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) return -EINVAL; - err = amdtp_stream_set_parameters(s, rate, - pcm_channels + midi_channels); + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + pcm_frame_multiplier = 2; + else + pcm_frame_multiplier = 1; + + err = amdtp_stream_set_parameters(s, rate, pcm_channels + midi_channels, + pcm_frame_multiplier); if (err < 0) return err; @@ -88,16 +96,6 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, p->pcm_channels = pcm_channels; p->midi_ports = midi_ports; - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk at higher - * sampling rate to transfer two PCM frames in one data block. - */ - if (double_pcm_frames) - p->frame_multiplier = 2; - else - p->frame_multiplier = 1; - /* init the position map for PCM and MIDI channels */ for (i = 0; i < pcm_channels; i++) p->pcm_positions[i] = i; @@ -346,23 +344,20 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; if (pcm) { write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); - pcm_frames += data_blocks * p->frame_multiplier; + pcm_frames += data_blocks * s->pcm_frame_multiplier; } else { write_pcm_silence(s, buf, data_blocks); } @@ -371,37 +366,34 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks, desc->data_block_counter); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; if (pcm) { read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); - pcm_frames += data_blocks * p->frame_multiplier; + pcm_frames += data_blocks * s->pcm_frame_multiplier; } if (p->midi_ports) { read_midi_messages(s, buf, data_blocks, desc->data_block_counter); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } /** diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 5fd2aeccdfc2..208f97cf8de6 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -14,9 +14,10 @@ #include <linux/tracepoint.h> TRACE_EVENT(amdtp_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index), - TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index), + TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index, u32 curr_cycle_time), + TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index, curr_cycle_time), TP_STRUCT__entry( + __field(unsigned int, cycle_time) __field(unsigned int, second) __field(unsigned int, cycle) __field(int, channel) @@ -31,6 +32,7 @@ TRACE_EVENT(amdtp_packet, __field(unsigned int, index) ), TP_fast_assign( + __entry->cycle_time = curr_cycle_time; __entry->second = cycles / CYCLES_PER_SECOND; __entry->cycle = cycles % CYCLES_PER_SECOND; __entry->channel = s->context->channel; @@ -53,7 +55,8 @@ TRACE_EVENT(amdtp_packet, __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s", + "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s", + __entry->cycle_time, __entry->second, __entry->cycle, __entry->src, diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9be2260e4ca2..a13c0b408aad 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -271,12 +271,14 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * @s: the AMDTP stream to configure * @rate: the sample rate * @data_block_quadlets: the size of a data block in quadlet unit + * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP + * events. * * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int data_block_quadlets) + unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier) { unsigned int sfc; @@ -298,6 +300,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, if (s->flags & CIP_BLOCKING) s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + s->pcm_frame_multiplier = pcm_frame_multiplier; + return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -348,27 +352,29 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); +#define prev_packet_desc(s, desc) \ + list_prev_entry_circular(desc, &s->packet_descs_list, link) + static void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, - unsigned int count) + unsigned int size, unsigned int pos, unsigned int count) { const unsigned int syt_interval = s->syt_interval; int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; if (desc->syt_offset != CIP_SYT_NO_INFO) desc->data_blocks = syt_interval; else desc->data_blocks = 0; - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } } static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, + unsigned int size, unsigned int pos, unsigned int count) { const enum cip_sfc sfc = s->sfc; @@ -376,7 +382,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; if (!cip_sfc_is_base_44100(sfc)) { // Sample_rate / 8000 is an integer, and precomputed. @@ -403,7 +409,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se state = phase; } - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } s->ctx_data.rx.data_block_state = state; @@ -449,8 +455,7 @@ static unsigned int calculate_syt_offset(unsigned int *last_syt_offset, } static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs, - const unsigned int seq_size, unsigned int seq_tail, - unsigned int count) + unsigned int size, unsigned int pos, unsigned int count) { const enum cip_sfc sfc = s->sfc; unsigned int last = s->ctx_data.rx.last_syt_offset; @@ -458,11 +463,11 @@ static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *desc int i; for (i = 0; i < count; ++i) { - struct seq_desc *desc = descs + seq_tail; + struct seq_desc *desc = descs + pos; desc->syt_offset = calculate_syt_offset(&last, &state, sfc); - seq_tail = (seq_tail + 1) % seq_size; + pos = (pos + 1) % size; } s->ctx_data.rx.last_syt_offset = last; @@ -497,7 +502,7 @@ static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle, static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head) { const unsigned int cache_size = s->ctx_data.tx.cache.size; - unsigned int cycles = s->ctx_data.tx.cache.tail; + unsigned int cycles = s->ctx_data.tx.cache.pos; if (cycles < head) cycles += cache_size; @@ -506,18 +511,17 @@ static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigne return cycles; } -static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsigned int desc_count) +static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count) { const unsigned int transfer_delay = s->transfer_delay; const unsigned int cache_size = s->ctx_data.tx.cache.size; struct seq_desc *cache = s->ctx_data.tx.cache.descs; - unsigned int cache_tail = s->ctx_data.tx.cache.tail; + unsigned int cache_pos = s->ctx_data.tx.cache.pos; bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); int i; for (i = 0; i < desc_count; ++i) { - struct seq_desc *dst = cache + cache_tail; - const struct pkt_desc *src = descs + i; + struct seq_desc *dst = cache + cache_pos; if (aware_syt && src->syt != CIP_SYT_NO_INFO) dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay); @@ -525,70 +529,68 @@ static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsi dst->syt_offset = CIP_SYT_NO_INFO; dst->data_blocks = src->data_blocks; - cache_tail = (cache_tail + 1) % cache_size; + cache_pos = (cache_pos + 1) % cache_size; + src = amdtp_stream_next_packet_desc(s, src); } - s->ctx_data.tx.cache.tail = cache_tail; + s->ctx_data.tx.cache.pos = cache_pos; } -static void pool_ideal_seq_descs(struct amdtp_stream *s, unsigned int count) +static void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { - struct seq_desc *descs = s->ctx_data.rx.seq.descs; - unsigned int seq_tail = s->ctx_data.rx.seq.tail; - const unsigned int seq_size = s->ctx_data.rx.seq.size; - - pool_ideal_syt_offsets(s, descs, seq_size, seq_tail, count); + pool_ideal_syt_offsets(s, descs, size, pos, count); if (s->flags & CIP_BLOCKING) - pool_blocking_data_blocks(s, descs, seq_size, seq_tail, count); + pool_blocking_data_blocks(s, descs, size, pos, count); else - pool_ideal_nonblocking_data_blocks(s, descs, seq_size, seq_tail, count); - - s->ctx_data.rx.seq.tail = (seq_tail + count) % seq_size; + pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count); } -static void pool_replayed_seq(struct amdtp_stream *s, unsigned int count) +static void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { struct amdtp_stream *target = s->ctx_data.rx.replay_target; const struct seq_desc *cache = target->ctx_data.tx.cache.descs; const unsigned int cache_size = target->ctx_data.tx.cache.size; - unsigned int cache_head = s->ctx_data.rx.cache_head; - struct seq_desc *descs = s->ctx_data.rx.seq.descs; - const unsigned int seq_size = s->ctx_data.rx.seq.size; - unsigned int seq_tail = s->ctx_data.rx.seq.tail; + unsigned int cache_pos = s->ctx_data.rx.cache_pos; int i; for (i = 0; i < count; ++i) { - descs[seq_tail] = cache[cache_head]; - seq_tail = (seq_tail + 1) % seq_size; - cache_head = (cache_head + 1) % cache_size; + descs[pos] = cache[cache_pos]; + cache_pos = (cache_pos + 1) % cache_size; + pos = (pos + 1) % size; } - s->ctx_data.rx.seq.tail = seq_tail; - s->ctx_data.rx.cache_head = cache_head; + s->ctx_data.rx.cache_pos = cache_pos; } -static void pool_seq_descs(struct amdtp_stream *s, unsigned int count) +static void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count) { struct amdtp_domain *d = s->domain; + void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, + unsigned int pos, unsigned int count); if (!d->replay.enable || !s->ctx_data.rx.replay_target) { - pool_ideal_seq_descs(s, count); + pool_seq_descs = pool_ideal_seq_descs; } else { if (!d->replay.on_the_fly) { - pool_replayed_seq(s, count); + pool_seq_descs = pool_replayed_seq; } else { struct amdtp_stream *tx = s->ctx_data.rx.replay_target; const unsigned int cache_size = tx->ctx_data.tx.cache.size; - const unsigned int cache_head = s->ctx_data.rx.cache_head; - unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_head); + const unsigned int cache_pos = s->ctx_data.rx.cache_pos; + unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos); if (cached_cycles > count && cached_cycles > cache_size / 2) - pool_replayed_seq(s, count); + pool_seq_descs = pool_replayed_seq; else - pool_ideal_seq_descs(s, count); + pool_seq_descs = pool_ideal_seq_descs; } } + + pool_seq_descs(s, descs, size, pos, count); } static void update_pcm_pointers(struct amdtp_stream *s, @@ -679,7 +681,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, struct fw_iso_packet *params, unsigned int header_length, unsigned int data_blocks, unsigned int data_block_counter, - unsigned int syt, unsigned int index) + unsigned int syt, unsigned int index, u32 curr_cycle_time) { unsigned int payload_length; __be32 *cip_header; @@ -696,7 +698,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, } trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks, - data_block_counter, s->packet_index, index); + data_block_counter, s->packet_index, index, curr_cycle_time); } static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, @@ -798,7 +800,8 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, const __be32 *ctx_header, unsigned int *data_blocks, unsigned int *data_block_counter, - unsigned int *syt, unsigned int packet_index, unsigned int index) + unsigned int *syt, unsigned int packet_index, unsigned int index, + u32 curr_cycle_time) { unsigned int payload_length; const __be32 *cip_header; @@ -843,7 +846,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, } trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks, - *data_block_counter, packet_index, index); + *data_block_counter, packet_index, index, curr_cycle_time); return 0; } @@ -851,10 +854,15 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, // In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On // the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent // it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. +static inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp) +{ + return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff); +} + static inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp) { u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK; - return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff); + return compute_ohci_iso_ctx_cycle_count(tstamp); } static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend) @@ -865,6 +873,14 @@ static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend) return cycle; } +static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend) +{ + if (minuend < subtrahend) + minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND; + + return minuend - subtrahend; +} + static int compare_ohci_cycle_count(u32 lval, u32 rval) { if (lval == rval) @@ -886,22 +902,23 @@ static inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp, return increment_ohci_cycle_count(cycle, queue_size); } -static int generate_device_pkt_descs(struct amdtp_stream *s, - struct pkt_desc *descs, - const __be32 *ctx_header, - unsigned int packets, - unsigned int *desc_count) +static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, + const __be32 *ctx_header, unsigned int packet_count, + unsigned int *desc_count) { unsigned int next_cycle = s->next_cycle; unsigned int dbc = s->data_block_counter; unsigned int packet_index = s->packet_index; unsigned int queue_size = s->queue_size; + u32 curr_cycle_time = 0; int i; int err; + if (trace_amdtp_packet_enabled()) + (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); + *desc_count = 0; - for (i = 0; i < packets; ++i) { - struct pkt_desc *desc = descs + *desc_count; + for (i = 0; i < packet_count; ++i) { unsigned int cycle; bool lost; unsigned int data_blocks; @@ -925,7 +942,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, desc->data_blocks = 0; desc->data_block_counter = dbc; desc->ctx_payload = NULL; - ++desc; + desc = amdtp_stream_next_packet_desc(s, desc); ++(*desc_count); } } else if (s->flags & CIP_JUMBO_PAYLOAD) { @@ -944,7 +961,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, } err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt, - packet_index, i); + packet_index, i, curr_cycle_time); if (err < 0) return err; @@ -958,6 +975,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, dbc = (dbc + desc->data_blocks) & 0xff; next_cycle = increment_ohci_cycle_count(next_cycle, 1); + desc = amdtp_stream_next_packet_desc(s, desc); ++(*desc_count); ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); packet_index = (packet_index + 1) % queue_size; @@ -980,20 +998,21 @@ static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, return syt & CIP_SYT_MASK; } -static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header, unsigned int packets) +static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, + const __be32 *ctx_header, unsigned int packet_count) { - struct pkt_desc *descs = s->pkt_descs; - const struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs; - const unsigned int seq_size = s->ctx_data.rx.seq.size; + struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs; + unsigned int seq_size = s->ctx_data.rx.seq.size; + unsigned int seq_pos = s->ctx_data.rx.seq.pos; unsigned int dbc = s->data_block_counter; - unsigned int seq_head = s->ctx_data.rx.seq.head; bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); int i; - for (i = 0; i < packets; ++i) { - struct pkt_desc *desc = descs + i; + pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count); + + for (i = 0; i < packet_count; ++i) { unsigned int index = (s->packet_index + i) % s->queue_size; - const struct seq_desc *seq = seq_descs + seq_head; + const struct seq_desc *seq = seq_descs + seq_pos; desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size); @@ -1014,13 +1033,14 @@ static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header, desc->ctx_payload = s->buffer.packets[index].buffer; - seq_head = (seq_head + 1) % seq_size; + seq_pos = (seq_pos + 1) % seq_size; + desc = amdtp_stream_next_packet_desc(s, desc); ++ctx_header; } s->data_block_counter = dbc; - s->ctx_data.rx.seq.head = seq_head; + s->ctx_data.rx.seq.pos = seq_pos; } static inline void cancel_stream(struct amdtp_stream *s) @@ -1031,17 +1051,85 @@ static inline void cancel_stream(struct amdtp_stream *s) WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } +static snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) +{ + unsigned int data_block_count = 0; + u32 latest_cycle; + u32 cycle_time; + u32 curr_cycle; + u32 cycle_gap; + int i, err; + + if (count == 0) + goto end; + + // Forward to the latest record. + for (i = 0; i < count - 1; ++i) + desc = amdtp_stream_next_packet_desc(s, desc); + latest_cycle = desc->cycle; + + err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time); + if (err < 0) + goto end; + + // Compute cycle count with lower 3 bits of second field and cycle field like timestamp + // format of 1394 OHCI isochronous context. + curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff); + + if (s->direction == AMDTP_IN_STREAM) { + // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since + // it corresponds to arrived isochronous packet. + if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0) + goto end; + cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle); + + // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated + // value expectedly corresponds to a few packets (0-2) since the packet arrived at + // the most recent isochronous cycle has been already processed. + for (i = 0; i < cycle_gap; ++i) { + desc = amdtp_stream_next_packet_desc(s, desc); + data_block_count += desc->data_blocks; + } + } else { + // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle + // since it was already scheduled. + if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0) + goto end; + cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle); + + // NOTE: use history of scheduled packets. + for (i = 0; i < cycle_gap; ++i) { + data_block_count += desc->data_blocks; + desc = prev_packet_desc(s, desc); + } + } +end: + return data_block_count * s->pcm_frame_multiplier; +} + static void process_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets) + const struct pkt_desc *desc, + unsigned int count) { struct snd_pcm_substream *pcm; - unsigned int pcm_frames; + int i; pcm = READ_ONCE(s->pcm); - pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm); - if (pcm) - update_pcm_pointers(s, pcm, pcm_frames); + s->process_ctx_payloads(s, desc, count, pcm); + + if (pcm) { + unsigned int data_block_count = 0; + + pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count); + + for (i = 0; i < count; ++i) { + data_block_count += desc->data_blocks; + desc = amdtp_stream_next_packet_desc(s, desc); + } + + update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier); + } } static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -1052,8 +1140,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ const __be32 *ctx_header = header; const unsigned int events_per_period = d->events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; + struct pkt_desc *desc = s->packet_descs_cursor; unsigned int pkt_header_length; unsigned int packets; + u32 curr_cycle_time; bool need_hw_irq; int i; @@ -1063,11 +1153,9 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ // Calculate the number of packets in buffer and check XRUN. packets = header_length / sizeof(*ctx_header); - pool_seq_descs(s, packets); - - generate_pkt_descs(s, ctx_header, packets); + generate_rx_packet_descs(s, desc, ctx_header, packets); - process_ctx_payloads(s, s->pkt_descs, packets); + process_ctx_payloads(s, desc, packets); if (!(s->flags & CIP_NO_HEADER)) pkt_header_length = IT_PKT_HEADER_SIZE_CIP; @@ -1084,8 +1172,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ need_hw_irq = false; } + if (trace_amdtp_packet_enabled()) + (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); + for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = s->pkt_descs + i; struct { struct fw_iso_packet params; __be32 header[CIP_HEADER_QUADLETS]; @@ -1094,7 +1184,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, desc->data_blocks, desc->data_block_counter, - desc->syt, i); + desc->syt, i, curr_cycle_time); if (s == s->domain->irq_target) { event_count += desc->data_blocks; @@ -1108,9 +1198,12 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ cancel_stream(s); return; } + + desc = amdtp_stream_next_packet_desc(s, desc); } s->ctx_data.rx.event_count = event_count; + s->packet_descs_cursor = desc; } static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -1188,6 +1281,9 @@ static void process_rx_packets_intermediately(struct fw_iso_context *context, u3 s->ready_processing = true; wake_up(&s->ready_wait); + if (d->replay.enable) + s->ctx_data.rx.cache_pos = 0; + process_rx_packets(context, tstamp, header_length, ctx_header, private_data); if (amdtp_streaming_error(s)) return; @@ -1204,7 +1300,8 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ { struct amdtp_stream *s = private_data; __be32 *ctx_header = header; - unsigned int packets; + struct pkt_desc *desc = s->packet_descs_cursor; + unsigned int packet_count; unsigned int desc_count; int i; int err; @@ -1213,10 +1310,10 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ return; // Calculate the number of packets in buffer and check XRUN. - packets = header_length / s->ctx_data.tx.ctx_header_size; + packet_count = header_length / s->ctx_data.tx.ctx_header_size; desc_count = 0; - err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets, &desc_count); + err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count); if (err < 0) { if (err != -EAGAIN) { cancel_stream(s); @@ -1225,13 +1322,17 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_ } else { struct amdtp_domain *d = s->domain; - process_ctx_payloads(s, s->pkt_descs, desc_count); + process_ctx_payloads(s, desc, desc_count); if (d->replay.enable) - cache_seq(s, s->pkt_descs, desc_count); + cache_seq(s, desc, desc_count); + + for (i = 0; i < desc_count; ++i) + desc = amdtp_stream_next_packet_desc(s, desc); + s->packet_descs_cursor = desc; } - for (i = 0; i < packets; ++i) { + for (i = 0; i < packet_count; ++i) { struct fw_iso_packet params = {0}; if (queue_in_packet(s, ¶ms) < 0) { @@ -1551,7 +1652,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; - int type, tag, err; + struct pkt_desc *descs; + int i, type, tag, err; mutex_lock(&s->mutex); @@ -1616,7 +1718,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, // possible to cache much unexpectedly. s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2, queue_size * 3 / 2); - s->ctx_data.tx.cache.tail = 0; + s->ctx_data.tx.cache.pos = 0; s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size, sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL); if (!s->ctx_data.tx.cache.descs) { @@ -1644,8 +1746,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, goto err_context; } s->ctx_data.rx.seq.size = queue_size; - s->ctx_data.rx.seq.tail = 0; - s->ctx_data.rx.seq.head = 0; + s->ctx_data.rx.seq.pos = 0; entry = &initial_state[s->sfc]; s->ctx_data.rx.data_block_state = entry->data_block; @@ -1660,12 +1761,24 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, else s->tag = TAG_CIP; - s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs), - GFP_KERNEL); - if (!s->pkt_descs) { + // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request + // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It + // could take a round over queue of AMDTP packet descriptors and small loss of history. For + // safe, keep more 8 elements for the queue, equivalent to 1 ms. + descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL); + if (!descs) { err = -ENOMEM; goto err_context; } + s->packet_descs = descs; + + INIT_LIST_HEAD(&s->packet_descs_list); + for (i = 0; i < s->queue_size; ++i) { + INIT_LIST_HEAD(&descs->link); + list_add_tail(&descs->link, &s->packet_descs_list); + ++descs; + } + s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link); s->packet_index = 0; do { @@ -1704,7 +1817,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, return 0; err_pkt_descs: - kfree(s->pkt_descs); + kfree(s->packet_descs); + s->packet_descs = NULL; err_context: if (s->direction == AMDTP_OUT_STREAM) { kfree(s->ctx_data.rx.seq.descs); @@ -1798,7 +1912,8 @@ static void amdtp_stream_stop(struct amdtp_stream *s) fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit); - kfree(s->pkt_descs); + kfree(s->packet_descs); + s->packet_descs = NULL; if (s->direction == AMDTP_OUT_STREAM) { kfree(s->ctx_data.rx.seq.descs); @@ -1917,7 +2032,6 @@ static int make_association(struct amdtp_domain *d) } rx->ctx_data.rx.replay_target = tx; - rx->ctx_data.rx.cache_head = 0; ++dst_index; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 1f957c946c95..b7ff44751ab9 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -103,14 +103,14 @@ struct pkt_desc { unsigned int data_blocks; unsigned int data_block_counter; __be32 *ctx_payload; + struct list_head link; }; struct amdtp_stream; -typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( - struct amdtp_stream *s, - const struct pkt_desc *desc, - unsigned int packets, - struct snd_pcm_substream *pcm); +typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s, + const struct pkt_desc *desc, + unsigned int count, + struct snd_pcm_substream *pcm); struct amdtp_domain; struct amdtp_stream { @@ -125,7 +125,9 @@ struct amdtp_stream { struct iso_packets_buffer buffer; unsigned int queue_size; int packet_index; - struct pkt_desc *pkt_descs; + struct pkt_desc *packet_descs; + struct list_head packet_descs_list; + struct pkt_desc *packet_descs_cursor; int tag; union { struct { @@ -145,7 +147,7 @@ struct amdtp_stream { struct { struct seq_desc *descs; unsigned int size; - unsigned int tail; + unsigned int pos; } cache; } tx; struct { @@ -159,8 +161,7 @@ struct amdtp_stream { struct { struct seq_desc *descs; unsigned int size; - unsigned int tail; - unsigned int head; + unsigned int pos; } seq; unsigned int data_block_state; @@ -168,7 +169,7 @@ struct amdtp_stream { unsigned int last_syt_offset; struct amdtp_stream *replay_target; - unsigned int cache_head; + unsigned int cache_pos; } rx; } ctx_data; @@ -188,6 +189,7 @@ struct amdtp_stream { struct snd_pcm_substream *pcm; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer; + unsigned int pcm_frame_multiplier; // To start processing content of packets at the same cycle in several contexts for // each direction. @@ -214,7 +216,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int data_block_quadlets); + unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); void amdtp_stream_update(struct amdtp_stream *s); @@ -277,6 +279,16 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, WRITE_ONCE(s->pcm, pcm); } +/** + * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet. + * @s: the AMDTP stream + * @desc: the descriptor of packet + * + * This macro computes next descriptor so that the list of descriptors behaves circular queue. + */ +#define amdtp_stream_next_packet_desc(s, desc) \ + list_next_entry_circular(desc, &s->packet_descs_list, link) + static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1; diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index 59b86c8d89e1..7db0024495b7 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -123,7 +123,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, * A first data channel is for MIDI messages, the rest is Multi Bit * Linear Audio data channel. */ - err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1); + err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1); if (err < 0) return err; @@ -341,16 +341,13 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, WRITE_ONCE(p->midi[port], midi); } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -360,21 +357,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, } read_midi_messages(s, buf, data_blocks); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -387,9 +381,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks, desc->data_block_counter); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c index 98177b0666d3..76c9d33ed572 100644 --- a/sound/firewire/fireface/amdtp-ff.c +++ b/sound/firewire/fireface/amdtp-ff.c @@ -24,7 +24,7 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate, p->pcm_channels = pcm_channels; data_channels = pcm_channels; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -112,16 +112,13 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -131,21 +128,18 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, } else { write_pcm_silence(s, buf, data_blocks); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __le32 *buf = (__le32 *)desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -153,9 +147,9 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); pcm_frames += data_blocks; } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index ea64a2a41eea..8a741b3b0436 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -15,16 +15,23 @@ #include "ff.h" +static bool has_msg(struct snd_ff *ff) +{ + if (ff->spec->protocol->has_msg) + return ff->spec->protocol->has_msg(ff); + else + return 0; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_ff *ff = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event; spin_lock_irq(&ff->lock); - while (!ff->dev_lock_changed) { + while (!ff->dev_lock_changed && !has_msg(ff)) { prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&ff->lock); schedule(); @@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&ff->lock); } - memset(&event, 0, sizeof(event)); - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = (ff->dev_lock_count > 0); - ff->dev_lock_changed = false; + if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) { + struct snd_firewire_event_lock_status ev = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + .status = (ff->dev_lock_count > 0), + }; - count = min_t(long, count, sizeof(event.lock_status)); + ff->dev_lock_changed = false; - spin_unlock_irq(&ff->lock); + spin_unlock_irq(&ff->lock); - if (copy_to_user(buf, &event, count)) - return -EFAULT; + if (copy_to_user(buf, &ev, sizeof(ev))) + return -EFAULT; + count = sizeof(ev); + } else if (has_msg(ff)) { + // NOTE: Acquired spin lock should be released before accessing to user space in the + // callback since the access can cause page fault. + count = ff->spec->protocol->copy_msg_to_user(ff, buf, count); + spin_unlock_irq(&ff->lock); + } else { + spin_unlock_irq(&ff->lock); + + count = 0; + } return count; } @@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &ff->hwdep_wait, wait); spin_lock_irq(&ff->lock); - if (ff->dev_lock_changed) + if (ff->dev_lock_changed || has_msg(ff)) events = EPOLLIN | EPOLLRDNORM; else events = 0; diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8900ffe517ed..efd59e9d9935 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff) // address. // A write transaction to clear registered higher 4 bytes of destination address // has an effect to suppress asynchronous transaction from device. -static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { int i; @@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, } const struct snd_ff_protocol snd_ff_protocol_ff800 = { - .handle_midi_msg = ff800_handle_midi_msg, + .handle_msg = ff800_handle_midi_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, @@ -534,6 +534,35 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); } +static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) +{ + struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]); + + if (substream != NULL) { + u8 byte = (quad >> (16 * port)) & 0x000000ff; + + snd_rawmidi_receive(substream, &byte, 1); + } +} + +#define FF400_QUEUE_SIZE 32 + +struct ff400_msg_parser { + struct { + u32 msg; + u32 tstamp; + } msgs[FF400_QUEUE_SIZE]; + size_t push_pos; + size_t pull_pos; +}; + +static bool ff400_has_msg(struct snd_ff *ff) +{ + struct ff400_msg_parser *parser = ff->msg_parser; + + return (parser->push_pos != parser->pull_pos); +} + // For Fireface 400, lower 4 bytes of destination address is configured by bit // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can // select one of 4 options: @@ -553,46 +582,147 @@ static void ff400_finish_session(struct snd_ff *ff) // input attenuation. This driver allocates destination address with '0000'0000 // in its lower offset and expects userspace application to configure the // register for it. -static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) + +// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of +// stereo physical port. +// - 0: Microphone input 0/1 +// - 1: Line input 0/1 +// - [2-4]: Line output 0-5 +// - 5: Headphone output 0/1 +// - 6: S/PDIF output 0/1 +// - [7-10]: ADAT output 0-7 +// +// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone +// input: +// +// - 0: 0.0 dB +// - 10: +10.0 dB +// - 11: +11.0 dB +// - 12: +12.0 dB +// - ... +// - 63: +63.0 dB: +// - 64: +64.0 dB: +// - 65: +65.0 dB: +// +// For signal level of line input: +// +// - 0: 0.0 dB +// - 1: +0.5 dB +// - 2: +1.0 dB +// - 3: +1.5 dB +// - ... +// - 34: +17.0 dB: +// - 35: +17.5 dB: +// - 36: +18.0 dB: +// +// For signal level of any type of output: +// +// - 63: -infinite +// - 62: -58.0 dB +// - 61: -56.0 dB +// - 60: -54.0 dB +// - 59: -53.0 dB +// - 58: -52.0 dB +// - ... +// - 7: -1.0 dB +// - 6: 0.0 dB +// - 5: +1.0 dB +// - ... +// - 2: +4.0 dB +// - 1: +5.0 dB +// - 0: +6.0 dB +// +// When the message is not for signal level operation, it's for MIDI bytes. When matching to +// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When +// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000. +#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000 +#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000 +#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000 +#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000 +#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00 +#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 +#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff +#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 +#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 + +static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { + bool need_hwdep_wake_up = false; int i; for (i = 0; i < length / 4; i++) { u32 quad = le32_to_cpu(buf[i]); - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } + if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) { + struct ff400_msg_parser *parser = ff->msg_parser; - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } + parser->msgs[parser->push_pos].msg = quad; + parser->msgs[parser->push_pos].tstamp = tstamp; + ++parser->push_pos; + if (parser->push_pos >= FF400_QUEUE_SIZE) + parser->push_pos = 0; + + need_hwdep_wake_up = true; + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) { + parse_midi_msg(ff, quad, 0); + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) { + parse_midi_msg(ff, quad, 1); } } + + if (need_hwdep_wake_up) + wake_up(&ff->hwdep_wait); +} + +static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count) +{ + struct snd_firewire_event_ff400_message ev = { + .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE, + .message_count = 0, + }; + struct ff400_msg_parser *parser = ff->msg_parser; + long consumed = 0; + long ret = 0; + + if (count < sizeof(ev) || parser->pull_pos == parser->push_pos) + return 0; + + count -= sizeof(ev); + consumed += sizeof(ev); + + while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) { + spin_unlock_irq(&ff->lock); + if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos, + sizeof(*parser->msgs))) + ret = -EFAULT; + spin_lock_irq(&ff->lock); + if (ret) + return ret; + + ++parser->pull_pos; + if (parser->pull_pos >= FF400_QUEUE_SIZE) + parser->pull_pos = 0; + ++ev.message_count; + count -= sizeof(*parser->msgs); + consumed += sizeof(*parser->msgs); + } + + spin_unlock_irq(&ff->lock); + if (copy_to_user(buf, &ev, sizeof(ev))) + ret = -EFAULT; + spin_lock_irq(&ff->lock); + if (ret) + return ret; + + return consumed; } const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .handle_midi_msg = ff400_handle_midi_msg, + .msg_parser_size = sizeof(struct ff400_msg_parser), + .has_msg = ff400_has_msg, + .copy_msg_to_user = ff400_copy_msg_to_user, + .handle_msg = ff400_handle_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 76c3eab36d4e..9947e0c2e0aa 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer // input attenuation. This driver allocates for the first option // (0x'....'....'0000'0000) and expects userspace application to configure the // register for it. -static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp) { u32 data = le32_to_cpu(*buf); unsigned int index = (data & 0x000000f0) >> 4; @@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff, } const struct snd_ff_protocol snd_ff_protocol_latter = { - .handle_midi_msg = latter_handle_midi_msg, + .handle_msg = latter_handle_midi_msg, .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index ee7122c461d4..6b89e39f4a43 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -125,19 +125,22 @@ static void transmit_midi1_msg(struct work_struct *work) transmit_midi_msg(ff, 1); } -static void handle_midi_msg(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) +static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode, + int destination, int source, int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) { struct snd_ff *ff = callback_data; __le32 *buf = data; + u32 tstamp = fw_request_get_timestamp(request); + unsigned long flag; fw_send_response(card, request, RCODE_COMPLETE); offset -= ff->async_handler.offset; - ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, - length); + + spin_lock_irqsave(&ff->lock, flag); + ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp); + spin_unlock_irqrestore(&ff->lock, flag); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -146,7 +149,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) int err; ff->async_handler.length = ff->spec->midi_addr_range; - ff->async_handler.address_callback = handle_midi_msg; + ff->async_handler.address_callback = handle_msg; ff->async_handler.callback_data = ff; midi_msg_region.start = 0x000100000000ull * i; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 7bf51d062021..448e972028d9 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card) snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); + kfree(ff->msg_parser); + mutex_destroy(&ff->mutex); fw_unit_put(ff->unit); } @@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e if (err < 0) goto error; + if (ff->spec->protocol->msg_parser_size > 0) { + ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL); + if (!ff->msg_parser) { + err = -ENOMEM; + goto error; + } + } + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 0535f0b58b67..7e42f5778a8a 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -97,6 +97,8 @@ struct snd_ff { wait_queue_head_t hwdep_wait; struct amdtp_domain domain; + + void *msg_parser; }; enum snd_ff_clock_src { @@ -110,8 +112,11 @@ enum snd_ff_clock_src { }; struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length); + size_t msg_parser_size; + bool (*has_msg)(struct snd_ff *ff); + long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count); + void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length, u32 tstamp); int (*fill_midi_msg)(struct snd_ff *ff, struct snd_rawmidi_substream *substream, unsigned int port); diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 2fb52f481d12..39ed57d2c5a0 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -73,7 +73,7 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, data_chunks = formats->msg_chunks + pcm_chunks; data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4); - err = amdtp_stream_set_parameters(s, rate, data_block_quadlets); + err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1); if (err < 0) return err; @@ -284,19 +284,19 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer, } } -static void probe_tracepoints_events(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets) +static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count) { int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; trace_data_block_sph(s, data_blocks, buf); trace_data_block_message(s, data_blocks, buf); + + desc = amdtp_stream_next_packet_desc(s, desc); } } @@ -328,13 +328,12 @@ static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *bu cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND; } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); struct amdtp_motu *p = s->protocol; + const struct pkt_desc *cursor = desc; unsigned int pcm_frames = 0; int i; @@ -342,8 +341,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND); // For data block processing. - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -356,22 +354,20 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, if (p->midi_ports) read_midi_messages(s, buf, data_blocks); - } - if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { - snd_motu_register_dsp_message_parser_parse(motu, descs, packets, - s->data_block_quadlets); - } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { - snd_motu_command_dsp_message_parser_parse(motu, descs, packets, - s->data_block_quadlets); + desc = amdtp_stream_next_packet_desc(s, desc); } + desc = cursor; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) + snd_motu_register_dsp_message_parser_parse(s, desc, count); + else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) + snd_motu_command_dsp_message_parser_parse(s, desc, count); + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) - probe_tracepoints_events(s, descs, packets); - - return pcm_frames; + probe_tracepoints_events(s, desc, count); } static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks, @@ -396,12 +392,11 @@ static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned i cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND; } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { struct amdtp_motu *p = s->protocol; + const struct pkt_desc *cursor = desc; unsigned int pcm_frames = 0; int i; @@ -409,8 +404,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND); // For data block processing. - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -425,14 +419,16 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, write_midi_messages(s, buf, data_blocks); write_sph(p->cache, buf, data_blocks, s->data_block_quadlets); + + desc = amdtp_stream_next_packet_desc(s, desc); } + desc = cursor; + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) - probe_tracepoints_events(s, descs, packets); - - return pcm_frames; + probe_tracepoints_events(s, desc, count); } int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c index 9efe4d364baf..5d8a86a12f1f 100644 --- a/sound/firewire/motu/motu-command-dsp-message-parser.c +++ b/sound/firewire/motu/motu-command-dsp-message-parser.c @@ -80,9 +80,11 @@ int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc #define FRAGMENTS_PER_VALUE 4 #define VALUES_AT_IMAGE_END 0xffffffffffffffff -void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets) +void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); + unsigned int data_block_quadlets = s->data_block_quadlets; struct msg_parser *parser = motu->message_parser; unsigned int interval = parser->interval; unsigned long flags; @@ -90,12 +92,13 @@ void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const stru spin_lock_irqsave(&parser->lock, flags); - for (i = 0; i < desc_count; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buffer = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; int j; + desc = amdtp_stream_next_packet_desc(s, desc); + for (j = 0; j < data_blocks; ++j) { u8 *b = (u8 *)buffer; buffer += data_block_quadlets; diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c index 0c587567540f..ef3b0b0f0dab 100644 --- a/sound/firewire/motu/motu-register-dsp-message-parser.c +++ b/sound/firewire/motu/motu-register-dsp-message-parser.c @@ -142,9 +142,11 @@ static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 i parser->push_pos = pos; } -void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets) +void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *desc, unsigned int count) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); + unsigned int data_block_quadlets = s->data_block_quadlets; struct msg_parser *parser = motu->message_parser; bool meter_pos_quirk = parser->meter_pos_quirk; unsigned int pos = parser->push_pos; @@ -153,12 +155,13 @@ void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const str spin_lock_irqsave(&parser->lock, flags); - for (i = 0; i < desc_count; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buffer = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; int j; + desc = amdtp_stream_next_packet_desc(s, desc); + for (j = 0; j < data_blocks; ++j) { u8 *b = (u8 *)buffer; u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 4189f2192284..3b1dc98a7be0 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -279,8 +279,8 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu); int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu); -void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *descs, unsigned int count); void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu, struct snd_firewire_motu_register_dsp_meter *meter); void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu, @@ -290,8 +290,8 @@ bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu); int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc); -void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, - unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s, + const struct pkt_desc *descs, unsigned int count); void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, struct snd_firewire_motu_command_dsp_meter *meter); diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 64d66a802545..0b42d6559008 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -29,7 +29,7 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) if (s->direction == AMDTP_IN_STREAM) data_channels += 2; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -176,16 +176,13 @@ static void read_status_messages(struct amdtp_stream *s, } } -static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -195,21 +192,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, } read_status_messages(s, buf, data_blocks); - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } -static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, - const struct pkt_desc *descs, - unsigned int packets, - struct snd_pcm_substream *pcm) +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { unsigned int pcm_frames = 0; int i; - for (i = 0; i < packets; ++i) { - const struct pkt_desc *desc = descs + i; + for (i = 0; i < count; ++i) { __be32 *buf = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; @@ -219,9 +213,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, } else { write_pcm_silence(s, buf, data_blocks); } - } - return pcm_frames; + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 547adbc22590..1f56fd33b9af 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -124,11 +124,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); /** * snd_hdac_stream_start - start a stream * @azx_dev: HD-audio core stream to start - * @fresh_start: false = wallclock timestamp relative to period wallclock * * Start a stream, set start_wallclk and set the running flag. */ -void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) +void snd_hdac_stream_start(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; int stripe_ctl; @@ -136,8 +135,6 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) trace_snd_hdac_stream_start(bus, azx_dev); azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK); - if (!fresh_start) - azx_dev->start_wallclk -= azx_dev->period_wallclk; /* enable SIE */ snd_hdac_chip_updatel(bus, INTCTL, @@ -966,7 +963,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare); void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start) { if (start) - snd_hdac_stream_start(azx_dev, true); + snd_hdac_stream_start(azx_dev); else snd_hdac_stream_stop(azx_dev); } diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 62a9615dcf52..60b0a70428d5 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -148,7 +148,7 @@ static void widget_release(struct kobject *kobj) kfree(kobj); } -static struct kobj_type widget_ktype = { +static const struct kobj_type widget_ktype = { .release = widget_release, .sysfs_ops = &widget_sysfs_ops, }; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 06d304db4183..886255a03e8b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -302,6 +302,20 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM This feature can impact power consumption as resources are kept reserved both at transmitter and receiver. +config SND_HDA_CTL_DEV_ID + bool "Use the device identifier field for controls" + depends on SND_HDA_INTEL + help + Say Y to use the device identifier field for (mixer) + controls (old behaviour until this option is available). + + When enabled, the multiple HDA codecs may set the device + field in control (mixer) element identifiers. The use + of this field is not recommended and defined for mixer controls. + + The old behaviour (Y) is obsolete and will be removed. Consider + to not enable this option. + endif endmenu diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index f7815ee24f83..75020edd39e7 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -58,7 +58,7 @@ static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL - { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; @@ -82,13 +82,13 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON - { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB }; static const struct reg_sequence cs35l41_hda_mute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB - { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute + { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute }; static void cs35l41_add_controls(struct cs35l41_hda *cs35l41) @@ -178,11 +178,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ @@ -191,10 +190,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, - cs35l41->amp_name, cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ @@ -209,11 +208,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, cs35l41->speaker_id, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); } /* try cirrus/part-dspN-fwtype-sub.wmfw */ @@ -224,29 +222,16 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, cs35l41->speaker_id, "bin"); + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, cs35l41->speaker_id, "bin"); - return 0; - } - - /* fallback try cirrus/part-dspN-fwtype.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); - if (!ret) { - /* fallback try cirrus/part-dspN-fwtype.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); - return 0; + return cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); } - dev_warn(cs35l41->dev, "Failed to request firmware\n"); - return ret; } @@ -258,9 +243,12 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, { int ret; - if (cs35l41->speaker_id > -1) - return cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); + if (cs35l41->speaker_id > -1) { + ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); + goto out; + + } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, @@ -268,10 +256,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, - cs35l41->amp_name, -1, "bin"); - return 0; + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + -1, "bin"); + goto out; } /* try cirrus/part-dspN-fwtype-sub.wmfw */ @@ -286,25 +275,35 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, - NULL, -1, "bin"); - return 0; + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, -1, + "bin"); } +out: + if (!ret) + return 0; + + /* Handle fallback */ + dev_warn(cs35l41->dev, "Falling back to default firmware.\n"); + + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + /* fallback try cirrus/part-dspN-fwtype.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); - if (!ret) { + if (!ret) /* fallback try cirrus/part-dspN-fwtype.bin */ - cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); - return 0; - } - - dev_warn(cs35l41->dev, "Failed to request firmware\n"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + if (ret) { + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n"); + } return ret; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2e728aad6771..9f79c0ac2bda 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3389,7 +3389,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; - if (addr > 0) + /* Do not use the id.device field for MIXER elements. + * This field is for real device numbers (like PCM) but codecs + * are hidden components from the user space view (unrelated + * to the mixer element identification). + */ + if (addr > 0 && codec->ctl_dev_id) kctl->id.device = addr; if (idx > 0) kctl->id.index = idx; @@ -3400,9 +3405,11 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, * the codec addr; if it still fails (or it's the * primary codec), then try another control index */ - if (!addr && codec->core.addr) + if (!addr && codec->core.addr) { addr = codec->core.addr; - else if (!idx && !knew->index) { + if (!codec->ctl_dev_id) + idx += 10 * addr; + } else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, knew->name, 0); if (idx <= 0) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 0ff286b7b66b..406779625fb5 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -257,7 +257,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) azx_dev = get_azx_dev(s); if (start) { azx_dev->insufficient = 1; - snd_hdac_stream_start(azx_stream(azx_dev), true); + snd_hdac_stream_start(azx_stream(azx_dev)); } else { snd_hdac_stream_stop(azx_stream(azx_dev)); } @@ -1231,6 +1231,7 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) continue; codec->jackpoll_interval = chip->jackpoll_interval; codec->beep_mode = chip->beep_mode; + codec->ctl_dev_id = chip->ctl_dev_id; codecs++; } } diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index f5bf295eb830..8556031bcd68 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -124,6 +124,7 @@ struct azx { /* HD codec */ int codec_probe_mask; /* copied from probe_mask option */ unsigned int beep_mode; + bool ctl_dev_id; #ifdef CONFIG_SND_HDA_PATCH_LOADER const struct firmware *fw; diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c index 5433f6227ac9..463ca06036bf 100644 --- a/sound/pci/hda/hda_cs_dsp_ctl.c +++ b/sound/pci/hda/hda_cs_dsp_ctl.c @@ -218,10 +218,10 @@ int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type, cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); mutex_unlock(&dsp->pwr_lock); - if (ret) + if (ret < 0) return ret; - if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) + if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS)) return 0; ctl = cs_ctl->priv; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 87002670c0c9..81c4a45254ff 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -50,6 +50,7 @@ #include <sound/intel-dsp-config.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> +#include <linux/apple-gmux.h> #include <linux/firmware.h> #include <sound/hda_codec.h> #include "hda_controller.h" @@ -119,6 +120,7 @@ static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = CONFIG_SND_HDA_INPUT_BEEP_MODE}; #endif static bool dmic_detect = 1; +static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -157,6 +159,8 @@ module_param(dmic_detect, bool, 0444); MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) " "(0=off, 1=on) (default=1); " "deprecated, use snd-intel-dspcfg.dsp_driver option instead"); +module_param(ctl_dev_id, bool, 0444); +MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address)."); #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); @@ -1463,7 +1467,7 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci) * vgaswitcheroo. */ if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) && - atpx_present()) + (atpx_present() || apple_gmux_detect(NULL, NULL))) return p; pci_dev_put(p); } @@ -2278,6 +2282,8 @@ static int azx_probe_continue(struct azx *chip) chip->beep_mode = beep_mode[dev]; #endif + chip->ctl_dev_id = ctl_dev_id; + /* create codec instances */ if (bus->codec_mask) { err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 976a112c7d00..c2bf86781894 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -582,12 +582,10 @@ static void hda_tegra_probe_work(struct work_struct *work) static int hda_tegra_remove(struct platform_device *pdev) { - int ret; - - ret = snd_card_free(dev_get_drvdata(&pdev->dev)); + snd_card_free(dev_get_drvdata(&pdev->dev)); pm_runtime_disable(&pdev->dev); - return ret; + return 0; } static void hda_tegra_shutdown(struct platform_device *pdev) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0a292bf271f2..acde4cd58785 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2455,7 +2455,7 @@ static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) { int status = 0; - unsigned int size = sizeof(dma_chan); + unsigned int size = sizeof(*dma_chan); codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); status = dspio_scp(codec, MASTERCONTROL, 0x20, diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 631a61ce52f4..a6cff2c46ac7 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -1046,16 +1046,13 @@ clean_open: /* called when module removal */ static void snd_ps3_driver_remove(struct ps3_system_bus_device *dev) { - int ret; pr_info("%s:start id=%d\n", __func__, dev->match_id); /* * ctl and preallocate buffer will be freed in * snd_card_free */ - ret = snd_card_free(the_card.card); - if (ret) - pr_info("%s: ctl freecard=%d\n", __func__, ret); + snd_card_free(the_card.card); dma_free_coherent(&dev->core, PAGE_SIZE, diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 1f4878ff7d37..676ad50638d0 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -16,6 +16,7 @@ #include <sound/pcm_params.h> #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> +#include <linux/dmi.h> #include <linux/module.h> #include "acp-mach.h" @@ -27,6 +28,7 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata rt5682s_max_data = { @@ -36,6 +38,7 @@ static struct acp_card_drvdata rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata rt5682s_rt1019_data = { @@ -45,6 +48,7 @@ static struct acp_card_drvdata rt5682s_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata max_nau8825_data = { @@ -56,6 +60,7 @@ static struct acp_card_drvdata max_nau8825_data = { .dmic_codec_id = DMIC, .soc_mclk = true, .platform = REMBRANDT, + .tdm_mode = false, }; static struct acp_card_drvdata rt5682s_rt1019_rmb_data = { @@ -67,6 +72,7 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = { .dmic_codec_id = DMIC, .soc_mclk = true, .platform = REMBRANDT, + .tdm_mode = false, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -90,6 +96,8 @@ static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + const struct dmi_system_id *dmi_id; + struct acp_card_drvdata *acp_card_drvdata; int ret; if (!pdev->id_entry) @@ -108,6 +116,11 @@ static int acp_asoc_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + acp_card_drvdata = card->drvdata; + dmi_id = dmi_first_match(acp_quirk_table); + if (dmi_id && dmi_id->driver_data) + acp_card_drvdata->tdm_mode = dmi_id->driver_data; + acp_legacy_dai_links_create(card); ret = devm_snd_soc_register_card(&pdev->dev, card); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index a78cf29387a7..b4dcce4fbae9 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -32,6 +32,20 @@ #define DUAL_CHANNEL 2 #define FOUR_CHANNEL 4 +#define TDM_MODE_ENABLE 1 + +const struct dmi_system_id acp_quirk_table[] = { + { + /* Google skyrim proto-0 */ + .matches = { + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"), + }, + .driver_data = (void *)TDM_MODE_ENABLE, + }, + {} +}; +EXPORT_SYMBOL_GPL(acp_quirk_table); + static struct snd_soc_jack pco_jack; static const unsigned int channels[] = { @@ -54,10 +68,11 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = { .mask = 0, }; -static int acp_clk_enable(struct acp_card_drvdata *drvdata) +static int acp_clk_enable(struct acp_card_drvdata *drvdata, + unsigned int srate, unsigned int bclk_ratio) { - clk_set_rate(drvdata->wclk, 48000); - clk_set_rate(drvdata->bclk, 48000 * 64); + clk_set_rate(drvdata->wclk, srate); + clk_set_rate(drvdata->bclk, srate * bclk_ratio); return clk_prepare_enable(drvdata->wclk); } @@ -86,34 +101,6 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) if (drvdata->hs_codec_id != RT5682) return -EINVAL; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBP_CFP); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK, - PCO_PLAT_CLK, RT5682_PLL_FREQ); - if (ret < 0) { - dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2, - RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret); - return ret; - } - - /* Set tdm/i2s1 master bclk ratio */ - ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64); - if (ret < 0) { - dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret); - return ret; - } - drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk"); drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); @@ -151,10 +138,15 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream) int ret; unsigned int fmt; + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; + if (drvdata->soc_mclk) - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; else - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; ret = snd_soc_dai_set_fmt(codec_dai, fmt); if (ret < 0) { @@ -168,16 +160,6 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - if (strcmp(codec_dai->name, "rt5682s-aif1") && strcmp(codec_dai->name, "rt5682s-aif2")) { - if (!drvdata->soc_mclk) { - ret = acp_clk_enable(drvdata); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret); - return ret; - } - } - } - return ret; } @@ -191,39 +173,36 @@ static void acp_card_shutdown(struct snd_pcm_substream *substream) clk_disable_unprepare(drvdata->wclk); } -static const struct snd_soc_ops acp_card_rt5682_ops = { - .startup = acp_card_hs_startup, - .shutdown = acp_card_shutdown, -}; - -/* Define RT5682S CODEC component*/ -SND_SOC_DAILINK_DEF(rt5682s, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1"))); - -static const struct snd_soc_dapm_route rt5682s_map[] = { - { "Headphone Jack", NULL, "HPOL" }, - { "Headphone Jack", NULL, "HPOR" }, - { "IN1P", NULL, "Headset Mic" }, -}; - -static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) +static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; - unsigned int fmt; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; + unsigned int fmt, srate, ch, format; - dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); + srate = params_rate(params); + ch = params_channels(params); + format = params_physical_width(params); - if (drvdata->hs_codec_id != RT5682S) - return -EINVAL; + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; if (drvdata->soc_mclk) - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; else - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } ret = snd_soc_dai_set_fmt(codec_dai, fmt); if (ret < 0) { @@ -231,14 +210,31 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK, + if (drvdata->tdm_mode) { + /** + * As codec supports slot 0 and slot 1 for playback and capture. + */ + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "set TDM slot err: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16); + if (ret < 0) { + dev_warn(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK, PCO_PLAT_CLK, RT5682_PLL_FREQ); if (ret < 0) { dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret); return ret; } - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2, + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2, RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret); @@ -246,13 +242,53 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) } /* Set tdm/i2s1 master bclk ratio */ - ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64); + ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format); if (ret < 0) { dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret); return ret; } if (!drvdata->soc_mclk) { + ret = acp_clk_enable(drvdata, srate, ch * format); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret); + return ret; + } + } + + return 0; +} + +static const struct snd_soc_ops acp_card_rt5682_ops = { + .startup = acp_card_hs_startup, + .shutdown = acp_card_shutdown, + .hw_params = acp_card_rt5682_hw_params, +}; + +/* Define RT5682S CODEC component*/ +SND_SOC_DAILINK_DEF(rt5682s, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1"))); + +static const struct snd_soc_dapm_route rt5682s_map[] = { + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + { "IN1P", NULL, "Headset Mic" }, +}; + +static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + int ret; + + dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); + + if (drvdata->hs_codec_id != RT5682S) + return -EINVAL; + + if (!drvdata->soc_mclk) { drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk"); drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); } @@ -281,8 +317,90 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map)); } +static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int ret; + unsigned int fmt, srate, ch, format; + + srate = params_rate(params); + ch = params_channels(params); + format = params_physical_width(params); + + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; + + if (drvdata->soc_mclk) + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + else + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + if (drvdata->tdm_mode) { + /** + * As codec supports slot 0 and slot 1 for playback and capture. + */ + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "set TDM slot err: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16); + if (ret < 0) { + dev_warn(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + + ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK, + PCO_PLAT_CLK, RT5682_PLL_FREQ); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2, + RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret); + return ret; + } + + /* Set tdm/i2s1 master bclk ratio */ + ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format); + if (ret < 0) { + dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret); + return ret; + } + + clk_set_rate(drvdata->wclk, srate); + clk_set_rate(drvdata->bclk, srate * ch * format); + + return 0; +} + static const struct snd_soc_ops acp_card_rt5682s_ops = { .startup = acp_card_hs_startup, + .hw_params = acp_card_rt5682s_hw_params, }; static const unsigned int dmic_channels[] = { @@ -351,19 +469,55 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream, struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; struct snd_soc_dai *codec_dai; - int srate, i, ret = 0; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int i, ret = 0; + unsigned int fmt, srate, ch, format; srate = params_rate(params); + ch = params_channels(params); + format = params_physical_width(params); if (drvdata->amp_codec_id != RT1019) return -EINVAL; + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; + + if (drvdata->soc_mclk) + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + else + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + if (drvdata->tdm_mode) { + /** + * As codec supports slot 2 and slot 3 for playback. + */ + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "set TDM slot err: %d\n", ret); + return ret; + } + } + for_each_rtd_codec_dais(rtd, i, codec_dai) { if (strcmp(codec_dai->name, "rt1019-aif")) continue; - ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK, - 64 * srate, 256 * srate); + if (drvdata->tdm_mode) + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK, + TDM_CHANNELS * format * srate, 256 * srate); + else + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK, + ch * format * srate, 256 * srate); + if (ret < 0) return ret; @@ -371,6 +525,41 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream, 256 * srate, SND_SOC_CLOCK_IN); if (ret < 0) return ret; + + if (drvdata->tdm_mode) { + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A + | SND_SOC_DAIFMT_NB_NF); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + /** + * As codec supports slot 2 for left channel playback. + */ + if (!strcmp(codec_dai->component->name, "i2c-10EC1019:00")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x4, 8, 16); + if (ret < 0) + break; + } + + /** + * As codec supports slot 3 for right channel playback. + */ + if (!strcmp(codec_dai->component->name, "i2c-10EC1019:01")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x8, 8, 16); + if (ret < 0) + break; + } + } + } + + if (!drvdata->soc_mclk) { + ret = acp_clk_enable(drvdata, srate, ch * format); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret); + return ret; + } } return 0; @@ -379,10 +568,6 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream, static int acp_card_amp_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_card *card = rtd->card; - struct acp_card_drvdata *drvdata = card->drvdata; - int ret = 0; runtime->hw.channels_max = DUAL_CHANNEL; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, @@ -390,14 +575,7 @@ static int acp_card_amp_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - if (!drvdata->soc_mclk) { - ret = acp_clk_enable(drvdata); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret); - return ret; - } - } - return ret; + return 0; } static const struct snd_soc_ops acp_card_rt1019_ops = { @@ -426,9 +604,61 @@ static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd) ARRAY_SIZE(max98360a_map)); } +static int acp_card_maxim_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + unsigned int fmt, srate, ch, format; + int ret; + + srate = params_rate(params); + ch = params_channels(params); + format = params_physical_width(params); + + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; + + if (drvdata->soc_mclk) + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + else + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + if (drvdata->tdm_mode) { + /** + * As codec supports slot 2 and slot 3 for playback. + */ + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "set TDM slot err: %d\n", ret); + return ret; + } + } + + if (!drvdata->soc_mclk) { + ret = acp_clk_enable(drvdata, srate, ch * format); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret); + return ret; + } + } + return 0; +} + static const struct snd_soc_ops acp_card_maxim_ops = { .startup = acp_card_amp_startup, .shutdown = acp_card_shutdown, + .hw_params = acp_card_maxim_hw_params, }; /* Declare nau8825 codec components */ @@ -446,7 +676,6 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) struct acp_card_drvdata *drvdata = card->drvdata; struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = codec_dai->component; - unsigned int fmt; int ret; dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); @@ -454,16 +683,6 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) if (drvdata->hs_codec_id != NAU8825) return -EINVAL; - if (drvdata->soc_mclk) - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; - else - fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; - - ret = snd_soc_dai_set_fmt(codec_dai, fmt); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); - return ret; - } ret = snd_soc_card_jack_new(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | @@ -492,8 +711,12 @@ static int acp_nau8825_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; + unsigned int fmt; ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, (48000 * 256), SND_SOC_CLOCK_IN); @@ -507,6 +730,44 @@ static int acp_nau8825_hw_params(struct snd_pcm_substream *substream, return ret; } + if (drvdata->tdm_mode) + fmt = SND_SOC_DAIFMT_DSP_A; + else + fmt = SND_SOC_DAIFMT_I2S; + + if (drvdata->soc_mclk) + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + else + fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + if (drvdata->tdm_mode) { + /** + * As codec supports slot 4 and slot 5 for playback and slot 6 for capture. + */ + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x30, 0xC0, 8, 16); + if (ret && ret != -ENOTSUPP) { + dev_err(rtd->dev, "set TDM slot err: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x40, 0x30, 8, 16); + if (ret < 0) { + dev_warn(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } return ret; } @@ -565,8 +826,12 @@ SND_SOC_DAILINK_DEF(i2s_hs, DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs"))); SND_SOC_DAILINK_DEF(sof_sp, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp"))); +SND_SOC_DAILINK_DEF(sof_sp_virtual, + DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp-virtual"))); SND_SOC_DAILINK_DEF(sof_hs, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs"))); +SND_SOC_DAILINK_DEF(sof_hs_virtual, + DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual"))); SND_SOC_DAILINK_DEF(sof_dmic, DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic"))); SND_SOC_DAILINK_DEF(pdm_dmic, @@ -596,8 +861,6 @@ static int acp_rtk_set_bias_level(struct snd_soc_card *card, switch (level) { case SND_SOC_BIAS_STANDBY: if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { - clk_set_rate(drvdata->wclk, 48000); - clk_set_rate(drvdata->bclk, 48000 * 64); /* Increase bclk's enable_count */ ret = clk_prepare_enable(drvdata->bclk); @@ -701,8 +964,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) if (drv_data->amp_cpu_id == I2S_SP) { links[i].name = "acp-amp-codec"; links[i].id = AMP_BE_ID; - links[i].cpus = sof_sp; - links[i].num_cpus = ARRAY_SIZE(sof_sp); + links[i].cpus = sof_sp_virtual; + links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual); links[i].platforms = sof_component; links[i].num_platforms = ARRAY_SIZE(sof_component); links[i].dpcm_playback = 1; @@ -733,8 +996,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) if (drv_data->amp_cpu_id == I2S_HS) { links[i].name = "acp-amp-codec"; links[i].id = AMP_BE_ID; - links[i].cpus = sof_hs; - links[i].num_cpus = ARRAY_SIZE(sof_hs); + links[i].cpus = sof_hs_virtual; + links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual); links[i].platforms = sof_component; links[i].num_platforms = ARRAY_SIZE(sof_component); links[i].dpcm_playback = 1; diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 20583ef902df..165f407697c0 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -18,6 +18,8 @@ #include <linux/module.h> #include <sound/soc.h> +#define TDM_CHANNELS 8 + enum be_id { HEADSET_BE_ID = 0, AMP_BE_ID, @@ -58,9 +60,11 @@ struct acp_card_drvdata { struct clk *wclk; struct clk *bclk; bool soc_mclk; + bool tdm_mode; }; int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); +extern const struct dmi_system_id acp_quirk_table[]; #endif diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index f19f064a7527..99a7d3879340 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -16,6 +16,7 @@ #include <sound/pcm_params.h> #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> +#include <linux/dmi.h> #include <linux/module.h> #include "acp-mach.h" @@ -27,6 +28,7 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -36,6 +38,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_rt1019_data = { @@ -45,6 +48,7 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_max_data = { @@ -54,6 +58,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, + .tdm_mode = false, }; static struct acp_card_drvdata sof_nau8825_data = { @@ -64,6 +69,7 @@ static struct acp_card_drvdata sof_nau8825_data = { .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, .soc_mclk = true, + .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { @@ -74,6 +80,7 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .amp_codec_id = RT1019, .dmic_codec_id = DMIC, .soc_mclk = true, + .tdm_mode = false, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -96,6 +103,8 @@ static int acp_sof_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; + const struct dmi_system_id *dmi_id; + struct acp_card_drvdata *acp_card_drvdata; int ret; if (!pdev->id_entry) @@ -114,6 +123,11 @@ static int acp_sof_probe(struct platform_device *pdev) card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + acp_card_drvdata = card->drvdata; + dmi_id = dmi_first_match(acp_quirk_table); + if (dmi_id && dmi_id->driver_data) + acp_card_drvdata->tdm_mode = dmi_id->driver_data; + acp_sofdsp_dai_links_create(card); ret = devm_snd_soc_register_card(&pdev->dev, card); diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 5e7f9c1c1b0e..6bf29b520511 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -11,7 +11,6 @@ #define ACP63_REG_START 0x1240000 #define ACP63_REG_END 0x1250200 #define ACP63_DEVS 3 -#define ACP63_PDM_MODE 1 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP_PGFSM_CNTL_POWER_ON_MASK 1 @@ -30,7 +29,7 @@ #define ACP_ERROR_STAT 29 #define PDM_DECIMATION_FACTOR 2 #define ACP_PDM_CLK_FREQ_MASK 7 -#define ACP_WOV_MISC_CTRL_MASK 0x10 +#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3) #define ACP_PDM_ENABLE 1 #define ACP_PDM_DISABLE 0 #define ACP_PDM_DMA_EN_STATUS 2 @@ -54,6 +53,11 @@ /* time in ms for runtime suspend delay */ #define ACP_SUSPEND_DELAY_MS 2000 +#define ACP63_DMIC_ADDR 2 +#define ACP63_PDM_MODE_DEVS 3 +#define ACP63_PDM_DEV_MASK 1 +#define ACP_DMIC_DEV 2 + enum acp_config { ACP_CONFIG_0 = 0, ACP_CONFIG_1, @@ -84,6 +88,7 @@ struct pdm_stream_instance { struct pdm_dev_data { u32 pdm_irq; void __iomem *acp63_base; + struct mutex *acp_lock; struct snd_pcm_substream *capture_stream; }; @@ -100,6 +105,9 @@ static inline void acp63_writel(u32 val, void __iomem *base_addr) struct acp63_dev_data { void __iomem *acp63_base; struct resource *res; - bool acp63_audio_mode; struct platform_device *pdev[ACP63_DEVS]; + struct mutex acp_lock; /* protect shared registers */ + u16 pdev_mask; + u16 pdev_count; + u16 pdm_dev_index; }; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 489f01a20699..e86f23d97584 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -116,6 +116,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) struct acp63_dev_data *adata; struct pdm_dev_data *ps_pdm_data; u32 val; + u16 pdev_index; adata = dev_id; if (!adata) @@ -123,7 +124,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (val & BIT(PDM_DMA_STAT)) { - ps_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev); + pdev_index = adata->pdm_dev_index; + ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ps_pdm_data->capture_stream) snd_pcm_period_elapsed(ps_pdm_data->capture_stream); @@ -132,17 +134,124 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static void get_acp63_device_config(u32 config, struct pci_dev *pci, + struct acp63_dev_data *acp_data) +{ + struct acpi_device *dmic_dev; + const union acpi_object *obj; + bool is_dmic_dev = false; + + dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); + if (dmic_dev) { + if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type", + ACPI_TYPE_INTEGER, &obj) && + obj->integer.value == ACP_DMIC_DEV) + is_dmic_dev = true; + } + + switch (config) { + case ACP_CONFIG_0: + case ACP_CONFIG_1: + case ACP_CONFIG_2: + case ACP_CONFIG_3: + case ACP_CONFIG_9: + case ACP_CONFIG_15: + dev_dbg(&pci->dev, "Audio Mode %d\n", config); + break; + default: + if (is_dmic_dev) { + acp_data->pdev_mask = ACP63_PDM_DEV_MASK; + acp_data->pdev_count = ACP63_PDM_MODE_DEVS; + } + break; + } +} + +static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, + struct device *parent, + struct fwnode_handle *fw_node, + char *name, unsigned int id, + const struct resource *res, + unsigned int num_res, + const void *data, + size_t size_data) +{ + pdevinfo->name = name; + pdevinfo->id = id; + pdevinfo->parent = parent; + pdevinfo->num_res = num_res; + pdevinfo->res = res; + pdevinfo->data = data; + pdevinfo->size_data = size_data; + pdevinfo->fwnode = fw_node; +} + +static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr) +{ + struct platform_device_info pdevinfo[ACP63_DEVS]; + struct device *parent; + int index; + int ret; + + parent = &pci->dev; + dev_dbg(&pci->dev, + "%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask, + adata->pdev_count); + if (adata->pdev_mask) { + adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); + if (!adata->res) { + ret = -ENOMEM; + goto de_init; + } + adata->res->flags = IORESOURCE_MEM; + adata->res->start = addr; + adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + } + + switch (adata->pdev_mask) { + case ACP63_PDM_DEV_MASK: + adata->pdm_dev_index = 0; + acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", + 0, adata->res, 1, &adata->acp_lock, + sizeof(adata->acp_lock)); + acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec", + 0, NULL, 0, NULL, 0); + acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach", + 0, NULL, 0, NULL, 0); + break; + default: + dev_dbg(&pci->dev, "No PDM devices found\n"); + return 0; + } + + for (index = 0; index < adata->pdev_count; index++) { + adata->pdev[index] = platform_device_register_full(&pdevinfo[index]); + if (IS_ERR(adata->pdev[index])) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo[index].name); + ret = PTR_ERR(adata->pdev[index]); + goto unregister_devs; + } + } + return 0; +unregister_devs: + for (--index; index >= 0; index--) + platform_device_unregister(adata->pdev[index]); +de_init: + if (acp63_deinit(adata->acp63_base, &pci->dev)) + dev_err(&pci->dev, "ACP de-init failed\n"); + return ret; +} + static int snd_acp63_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct acp63_dev_data *adata; - struct platform_device_info pdevinfo[ACP63_DEVS]; - int index, ret; - int val = 0x00; - struct acpi_device *adev; - const union acpi_object *obj; u32 addr; - unsigned int irqflags; + u32 irqflags; + int val; + int ret; irqflags = IRQF_SHARED; /* Pink Sardine device check */ @@ -179,86 +288,28 @@ static int snd_acp63_probe(struct pci_dev *pci, } pci_set_master(pci); pci_set_drvdata(pci, adata); + mutex_init(&adata->acp_lock); ret = acp63_init(adata->acp63_base, &pci->dev); if (ret) goto release_regions; + ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler, + irqflags, "ACP_PCI_IRQ", adata); + if (ret) { + dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); + goto de_init; + } val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG); - switch (val) { - case ACP_CONFIG_0: - case ACP_CONFIG_1: - case ACP_CONFIG_2: - case ACP_CONFIG_3: - case ACP_CONFIG_9: - case ACP_CONFIG_15: - dev_info(&pci->dev, "Audio Mode %d\n", val); - break; - default: - - /* Checking DMIC hardware*/ - adev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), 0x02, 0); - - if (!adev) - break; - - if (!acpi_dev_get_property(adev, "acp-audio-device-type", - ACPI_TYPE_INTEGER, &obj) && - obj->integer.value == 2) { - adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); - if (!adata->res) { - ret = -ENOMEM; - goto de_init; - } - - adata->res->name = "acp_iomem"; - adata->res->flags = IORESOURCE_MEM; - adata->res->start = addr; - adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START); - adata->acp63_audio_mode = ACP63_PDM_MODE; - - memset(&pdevinfo, 0, sizeof(pdevinfo)); - pdevinfo[0].name = "acp_ps_pdm_dma"; - pdevinfo[0].id = 0; - pdevinfo[0].parent = &pci->dev; - pdevinfo[0].num_res = 1; - pdevinfo[0].res = adata->res; - - pdevinfo[1].name = "dmic-codec"; - pdevinfo[1].id = 0; - pdevinfo[1].parent = &pci->dev; - - pdevinfo[2].name = "acp_ps_mach"; - pdevinfo[2].id = 0; - pdevinfo[2].parent = &pci->dev; - - for (index = 0; index < ACP63_DEVS; index++) { - adata->pdev[index] = - platform_device_register_full(&pdevinfo[index]); - - if (IS_ERR(adata->pdev[index])) { - dev_err(&pci->dev, - "cannot register %s device\n", - pdevinfo[index].name); - ret = PTR_ERR(adata->pdev[index]); - goto unregister_devs; - } - ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler, - irqflags, "ACP_PCI_IRQ", adata); - if (ret) { - dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); - goto unregister_devs; - } - } - } - break; + get_acp63_device_config(val, pci, adata); + ret = create_acp63_platform_devs(pci, adata, addr); + if (ret < 0) { + dev_err(&pci->dev, "ACP platform devices creation failed\n"); + goto de_init; } pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pci->dev); pm_runtime_put_noidle(&pci->dev); pm_runtime_allow(&pci->dev); return 0; -unregister_devs: - for (--index; index >= 0; index--) - platform_device_unregister(adata->pdev[index]); de_init: if (acp63_deinit(adata->acp63_base, &pci->dev)) dev_err(&pci->dev, "ACP de-init failed\n"); @@ -305,10 +356,8 @@ static void snd_acp63_remove(struct pci_dev *pci) int ret, index; adata = pci_get_drvdata(pci); - if (adata->acp63_audio_mode == ACP63_PDM_MODE) { - for (index = 0; index < ACP63_DEVS; index++) - platform_device_unregister(adata->pdev[index]); - } + for (index = 0; index < adata->pdev_count; index++) + platform_device_unregister(adata->pdev[index]); ret = acp63_deinit(adata->acp63_base, &pci->dev); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index eea71a9d2ef1..454dab062e4f 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -7,6 +7,7 @@ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/bitfield.h> #include <linux/err.h> #include <linux/io.h> #include <sound/pcm_params.h> @@ -18,6 +19,10 @@ #define DRV_NAME "acp_ps_pdm_dma" +static int pdm_gain = 3; +module_param(pdm_gain, int, 0644); +MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)"); + static const struct snd_pcm_hardware acp63_pdm_hardware_capture = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -55,26 +60,31 @@ static void acp63_enable_pdm_clock(void __iomem *acp_base) acp63_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL); pdm_ctrl = acp63_readl(acp_base + ACP_WOV_MISC_CTRL); - pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK; + pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL; + pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3)); acp63_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL); } -static void acp63_enable_pdm_interrupts(void __iomem *acp_base) +static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata) { u32 ext_int_ctrl; - ext_int_ctrl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + mutex_lock(adata->acp_lock); + ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); ext_int_ctrl |= PDM_DMA_INTR_MASK; - acp63_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL); + acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + mutex_unlock(adata->acp_lock); } -static void acp63_disable_pdm_interrupts(void __iomem *acp_base) +static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata) { u32 ext_int_ctrl; - ext_int_ctrl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + mutex_lock(adata->acp_lock); + ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); ext_int_ctrl &= ~PDM_DMA_INTR_MASK; - acp63_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL); + acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL); + mutex_unlock(adata->acp_lock); } static bool acp63_check_pdm_dma_status(void __iomem *acp_base) @@ -196,7 +206,7 @@ static int acp63_pdm_dma_open(struct snd_soc_component *component, return ret; } - acp63_enable_pdm_interrupts(adata->acp63_base); + acp63_enable_pdm_interrupts(adata); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) adata->capture_stream = substream; @@ -272,7 +282,7 @@ static int acp63_pdm_dma_close(struct snd_soc_component *component, struct pdm_dev_data *adata = dev_get_drvdata(component->dev); struct snd_pcm_runtime *runtime = substream->runtime; - acp63_disable_pdm_interrupts(adata->acp63_base); + acp63_disable_pdm_interrupts(adata); adata->capture_stream = NULL; kfree(runtime->private_data); return 0; @@ -353,6 +363,10 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev) struct pdm_dev_data *adata; int status; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "platform_data not retrieved\n"); + return -ENODEV; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); @@ -368,7 +382,7 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev) return -ENOMEM; adata->capture_stream = NULL; - + adata->acp_lock = pdev->dev.platform_data; dev_set_drvdata(&pdev->dev, adata); status = devm_snd_soc_register_component(&pdev->dev, &acp63_pdm_component, @@ -408,7 +422,7 @@ static int __maybe_unused acp63_pdm_resume(struct device *dev) acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len, period_bytes, adata->acp63_base); } - acp63_enable_pdm_interrupts(adata->acp63_base); + acp63_enable_pdm_interrupts(adata); return 0; } @@ -417,7 +431,7 @@ static int __maybe_unused acp63_pdm_suspend(struct device *dev) struct pdm_dev_data *adata; adata = dev_get_drvdata(dev); - acp63_disable_pdm_interrupts(adata->acp63_base); + acp63_disable_pdm_interrupts(adata); return 0; } @@ -426,7 +440,7 @@ static int __maybe_unused acp63_pdm_runtime_resume(struct device *dev) struct pdm_dev_data *adata; adata = dev_get_drvdata(dev); - acp63_enable_pdm_interrupts(adata->acp63_base); + acp63_enable_pdm_interrupts(adata); return 0; } diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c index aa38cef1776d..4ba83689482a 100644 --- a/sound/soc/amd/raven/acp3x-i2s.c +++ b/sound/soc/amd/raven/acp3x-i2s.c @@ -315,16 +315,8 @@ static int acp3x_dai_probe(struct platform_device *pdev) return 0; } -static int acp3x_dai_remove(struct platform_device *pdev) -{ - /* As we use devm_ memory alloc there is nothing TBD here */ - - return 0; -} - static struct platform_driver acp3x_dai_driver = { .probe = acp3x_dai_probe, - .remove = acp3x_dai_remove, .driver = { .name = "acp3x_i2s_playcap", }, diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index 7203c6488df0..0d8b693aecc9 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -6,6 +6,7 @@ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/bitfield.h> #include <linux/err.h> #include <linux/io.h> #include <linux/pm_runtime.h> @@ -17,6 +18,10 @@ #define DRV_NAME "acp_rn_pdm_dma" +static int pdm_gain = 3; +module_param(pdm_gain, int, 0644); +MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)"); + static const struct snd_pcm_hardware acp_pdm_hardware_capture = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -80,7 +85,8 @@ static void enable_pdm_clock(void __iomem *acp_base) rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL); pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL); - pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK; + pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL; + pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3)); rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL); } diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h index ca586603d720..7d0f4e6a2834 100644 --- a/sound/soc/amd/renoir/rn_acp3x.h +++ b/sound/soc/amd/renoir/rn_acp3x.h @@ -34,7 +34,7 @@ #define ACP_ERROR_STAT 29 #define PDM_DECIMATION_FACTOR 0x2 #define ACP_PDM_CLK_FREQ_MASK 0x07 -#define ACP_WOV_MISC_CTRL_MASK 0x10 +#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3) #define ACP_PDM_ENABLE 0x01 #define ACP_PDM_DISABLE 0x00 #define ACP_PDM_DMA_EN_STATUS 0x02 diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index acecd6a4ec4b..294dd7fb43c9 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -7,6 +7,7 @@ #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/bitfield.h> #include <linux/err.h> #include <linux/io.h> #include <sound/pcm_params.h> @@ -18,6 +19,10 @@ #define DRV_NAME "acp_yc_pdm_dma" +static int pdm_gain = 3; +module_param(pdm_gain, int, 0644); +MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)"); + static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -55,7 +60,8 @@ static void acp6x_enable_pdm_clock(void __iomem *acp_base) acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL); pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL); - pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK; + pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL; + pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3)); acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL); } diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h index 74b596e6807a..036207568c04 100644 --- a/sound/soc/amd/yc/acp6x.h +++ b/sound/soc/amd/yc/acp6x.h @@ -31,7 +31,7 @@ #define ACP_ERROR_STAT 29 #define PDM_DECIMATION_FACTOR 2 #define ACP_PDM_CLK_FREQ_MASK 7 -#define ACP_WOV_MISC_CTRL_MASK 0x10 +#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3) #define ACP_PDM_ENABLE 1 #define ACP_PDM_DISABLE 0 #define ACP_PDM_DMA_EN_STATUS 2 diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 87d6d6ed026b..9883e6867fd1 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -616,11 +616,6 @@ unregister_codec: return ret; } -static int atmel_classd_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver atmel_classd_driver = { .driver = { .name = "atmel-classd", @@ -628,7 +623,6 @@ static struct platform_driver atmel_classd_driver = { .pm = &snd_soc_pm_ops, }, .probe = atmel_classd_probe, - .remove = atmel_classd_remove, }; module_platform_driver(atmel_classd_driver); diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index 77ff12baead5..12cd40b15644 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -692,11 +692,6 @@ unregister_codec: return ret; } -static int atmel_pdmic_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver atmel_pdmic_driver = { .driver = { .name = "atmel-pdmic", @@ -704,7 +699,6 @@ static struct platform_driver atmel_pdmic_driver = { .pm = &snd_soc_pm_ops, }, .probe = atmel_pdmic_probe, - .remove = atmel_pdmic_remove, }; module_platform_driver(atmel_pdmic_driver); diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index 44aefbd5b62c..cf4084dcbd5e 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -11,6 +11,7 @@ #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/core.h> @@ -112,10 +113,10 @@ struct mchp_pdmc { struct clk *pclk; struct clk *gclk; u32 pdmcen; + u32 suspend_irq; int mic_no; int sinc_order; bool audio_filter_en; - u8 gclk_enabled:1; }; static const char *const mchp_pdmc_sinc_filter_order_text[] = { @@ -454,13 +455,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - int ret; - - ret = clk_prepare_enable(dd->pclk); - if (ret) { - dev_err(dd->dev, "failed to enable the peripheral clock: %d\n", ret); - return ret; - } regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST); @@ -470,14 +464,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream, return 0; } -static void mchp_pdmc_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - - clk_disable_unprepare(dd->pclk); -} - static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai) { struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); @@ -594,11 +580,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); } - if (dd->gclk_enabled) { - clk_disable_unprepare(dd->gclk); - dd->gclk_enabled = 0; - } - for (osr_start = dd->audio_filter_en ? 64 : 8; osr_start <= 256 && best_diff_rate; osr_start *= 2) { long round_rate; @@ -620,8 +601,12 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* CLK is enabled by runtime PM. */ + clk_disable_unprepare(dd->gclk); + /* set the rate */ ret = clk_set_rate(dd->gclk, gclk_rate); + clk_prepare_enable(dd->gclk); if (ret) { dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n", gclk_rate, ret); @@ -636,9 +621,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, mr_val |= MCHP_PDMC_MR_CHUNK(dd->addr.maxburst); dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst); - clk_prepare_enable(dd->gclk); - dd->gclk_enabled = 1; - snd_soc_component_update_bits(comp, MCHP_PDMC_MR, MCHP_PDMC_MR_OSR_MASK | MCHP_PDMC_MR_SINCORDER_MASK | @@ -650,19 +632,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, return 0; } -static int mchp_pdmc_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); - - if (dd->gclk_enabled) { - clk_disable_unprepare(dd->gclk); - dd->gclk_enabled = 0; - } - - return 0; -} - static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -673,22 +642,27 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, #endif switch (cmd) { - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_START: /* Enable overrun and underrun error interrupts */ - regmap_write(dd->regmap, MCHP_PDMC_IER, + regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq | MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + dd->suspend_irq = 0; + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, MCHP_PDMC_MR_PDMCEN_MASK, dd->pdmcen); break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq); + fallthrough; + case SNDRV_PCM_TRIGGER_STOP: /* Disable overrun and underrun error interrupts */ - regmap_write(dd->regmap, MCHP_PDMC_IDR, + regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq | MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, MCHP_PDMC_MR_PDMCEN_MASK, 0); break; @@ -711,9 +685,7 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { .set_fmt = mchp_pdmc_set_fmt, .startup = mchp_pdmc_startup, - .shutdown = mchp_pdmc_shutdown, .hw_params = mchp_pdmc_hw_params, - .hw_free = mchp_pdmc_hw_free, .trigger = mchp_pdmc_trigger, }; @@ -864,6 +836,7 @@ static const struct regmap_config mchp_pdmc_regmap_config = { .readable_reg = mchp_pdmc_readable_reg, .writeable_reg = mchp_pdmc_writeable_reg, .precious_reg = mchp_pdmc_precious_reg, + .cache_type = REGCACHE_FLAT, }; static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) @@ -970,6 +943,49 @@ static struct snd_dmaengine_pcm_config mchp_pdmc_config = { .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, }; +static int mchp_pdmc_runtime_suspend(struct device *dev) +{ + struct mchp_pdmc *dd = dev_get_drvdata(dev); + + regcache_cache_only(dd->regmap, true); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->pclk); + + return 0; +} + +static int mchp_pdmc_runtime_resume(struct device *dev) +{ + struct mchp_pdmc *dd = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dd->pclk); + if (ret) { + dev_err(dd->dev, + "failed to enable the peripheral clock: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(dd->gclk); + if (ret) { + dev_err(dd->dev, + "failed to enable generic clock: %d\n", ret); + goto disable_pclk; + } + + regcache_cache_only(dd->regmap, false); + regcache_mark_dirty(dd->regmap); + ret = regcache_sync(dd->regmap); + if (ret) { + regcache_cache_only(dd->regmap, true); + clk_disable_unprepare(dd->gclk); +disable_pclk: + clk_disable_unprepare(dd->pclk); + } + + return ret; +} + static int mchp_pdmc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1039,18 +1055,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev) dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR; platform_set_drvdata(pdev, dd); + pm_runtime_enable(dd->dev); + if (!pm_runtime_enabled(dd->dev)) { + ret = mchp_pdmc_runtime_resume(dd->dev); + if (ret) + return ret; + } + /* register platform */ ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0); if (ret) { dev_err(dev, "could not register platform: %d\n", ret); - return ret; + goto pm_runtime_suspend; } ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component, &mchp_pdmc_dai, 1); if (ret) { dev_err(dev, "could not register CPU DAI: %d\n", ret); - return ret; + goto pm_runtime_suspend; } /* print IP version */ @@ -1059,6 +1082,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev) version & MCHP_PDMC_VER_VERSION); return 0; + +pm_runtime_suspend: + if (!pm_runtime_status_suspended(dd->dev)) + mchp_pdmc_runtime_suspend(dd->dev); + pm_runtime_disable(dd->dev); + + return ret; +} + +static int mchp_pdmc_remove(struct platform_device *pdev) +{ + struct mchp_pdmc *dd = platform_get_drvdata(pdev); + + if (!pm_runtime_status_suspended(dd->dev)) + mchp_pdmc_runtime_suspend(dd->dev); + + pm_runtime_disable(dd->dev); + + return 0; } static const struct of_device_id mchp_pdmc_of_match[] = { @@ -1070,13 +1112,20 @@ static const struct of_device_id mchp_pdmc_of_match[] = { }; MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match); +static const struct dev_pm_ops mchp_pdmc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume, + NULL) +}; + static struct platform_driver mchp_pdmc_driver = { .driver = { .name = "mchp-pdmc", .of_match_table = of_match_ptr(mchp_pdmc_of_match), - .pm = &snd_soc_pm_ops, + .pm = pm_ptr(&mchp_pdmc_pm_ops), }, .probe = mchp_pdmc_probe, + .remove = mchp_pdmc_remove, }; module_platform_driver(mchp_pdmc_driver); diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index ec0705cc40fa..eb0c0ef4541e 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/spinlock.h> @@ -192,6 +193,43 @@ static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg) } } +static bool mchp_spdifrx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFRX_IMR: + case SPDIFRX_ISR: + case SPDIFRX_RSR: + case SPDIFRX_CHSR(0, 0): + case SPDIFRX_CHSR(0, 1): + case SPDIFRX_CHSR(0, 2): + case SPDIFRX_CHSR(0, 3): + case SPDIFRX_CHSR(0, 4): + case SPDIFRX_CHSR(0, 5): + case SPDIFRX_CHUD(0, 0): + case SPDIFRX_CHUD(0, 1): + case SPDIFRX_CHUD(0, 2): + case SPDIFRX_CHUD(0, 3): + case SPDIFRX_CHUD(0, 4): + case SPDIFRX_CHUD(0, 5): + case SPDIFRX_CHSR(1, 0): + case SPDIFRX_CHSR(1, 1): + case SPDIFRX_CHSR(1, 2): + case SPDIFRX_CHSR(1, 3): + case SPDIFRX_CHSR(1, 4): + case SPDIFRX_CHSR(1, 5): + case SPDIFRX_CHUD(1, 0): + case SPDIFRX_CHUD(1, 1): + case SPDIFRX_CHUD(1, 2): + case SPDIFRX_CHUD(1, 3): + case SPDIFRX_CHUD(1, 4): + case SPDIFRX_CHUD(1, 5): + case SPDIFRX_VERSION: + return true; + default: + return false; + } +} + static const struct regmap_config mchp_spdifrx_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -200,6 +238,8 @@ static const struct regmap_config mchp_spdifrx_regmap_config = { .readable_reg = mchp_spdifrx_readable_reg, .writeable_reg = mchp_spdifrx_writeable_reg, .precious_reg = mchp_spdifrx_precious_reg, + .volatile_reg = mchp_spdifrx_volatile_reg, + .cache_type = REGCACHE_FLAT, }; #define SPDIFRX_GCLK_RATIO_MIN (12 * 64) @@ -209,17 +249,34 @@ static const struct regmap_config mchp_spdifrx_regmap_config = { #define SPDIFRX_CHANNELS 2 +/** + * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status + * @data: channel status bits + * @done: completion to signal channel status bits acquisition done + */ struct mchp_spdifrx_ch_stat { unsigned char data[SPDIFRX_CS_BITS / 8]; struct completion done; }; +/** + * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data + * @data: user data bits + * @done: completion to signal user data bits acquisition done + */ struct mchp_spdifrx_user_data { unsigned char data[SPDIFRX_UD_BITS / 8]; struct completion done; - spinlock_t lock; /* protect access to user data */ }; +/** + * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure + * @ch_stat: array of channel statuses + * @user_data: array of user data + * @ulock: ulock bit status + * @badf: badf bit status + * @signal: signal bit status + */ struct mchp_spdifrx_mixer_control { struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS]; struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS]; @@ -228,17 +285,26 @@ struct mchp_spdifrx_mixer_control { bool signal; }; +/** + * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure + * @capture: DAI DMA configuration data + * @control: mixer controls + * @mlock: mutex to protect concurency b/w configuration and control APIs + * @dev: struct device + * @regmap: regmap for this device + * @pclk: peripheral clock + * @gclk: generic clock + * @trigger_enabled: true if enabled though trigger() ops + */ struct mchp_spdifrx_dev { struct snd_dmaengine_dai_dma_data capture; struct mchp_spdifrx_mixer_control control; - spinlock_t blockend_lock; /* protect access to blockend_refcount */ - int blockend_refcount; + struct mutex mlock; struct device *dev; struct regmap *regmap; struct clk *pclk; struct clk *gclk; - unsigned int fmt; - unsigned int gclk_enabled:1; + unsigned int trigger_enabled; }; static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev, @@ -275,37 +341,11 @@ static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev, } } -/* called from non-atomic context only */ -static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount++; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 1) - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - -/* called from atomic/non-atomic context */ -static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount--; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 0) - regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) { struct mchp_spdifrx_dev *dev = dev_id; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; - u32 sr, imr, pending, idr = 0; + u32 sr, imr, pending; irqreturn_t ret = IRQ_NONE; int ch; @@ -320,13 +360,10 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_BLOCKEND) { for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { - spin_lock(&ctrl->user_data[ch].lock); mchp_spdifrx_channel_user_data_read(dev, ch); - spin_unlock(&ctrl->user_data[ch].lock); - complete(&ctrl->user_data[ch].done); } - mchp_spdifrx_isr_blockend_dis(dev); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); ret = IRQ_HANDLED; } @@ -334,7 +371,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_CSC(ch)) { mchp_spdifrx_channel_status_read(dev, ch); complete(&ctrl->ch_stat[ch].done); - idr |= SPDIFRX_IR_CSC(ch); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch)); ret = IRQ_HANDLED; } } @@ -344,8 +381,6 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; } - regmap_write(dev->regmap, SPDIFRX_IDR, idr); - return ret; } @@ -353,47 +388,40 @@ static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); - u32 mr; - int running; - int ret; - - regmap_read(dev->regmap, SPDIFRX_MR, &mr); - running = !!(mr & SPDIFRX_MR_RXEN_ENABLE); + int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!running) { - mr &= ~SPDIFRX_MR_RXEN_MASK; - mr |= SPDIFRX_MR_RXEN_ENABLE; - /* enable overrun interrupts */ - regmap_write(dev->regmap, SPDIFRX_IER, - SPDIFRX_IR_OVERRUN); - } + mutex_lock(&dev->mlock); + /* Enable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_OVERRUN); + + /* Enable receiver. */ + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_ENABLE); + dev->trigger_enabled = true; + mutex_unlock(&dev->mlock); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (running) { - mr &= ~SPDIFRX_MR_RXEN_MASK; - mr |= SPDIFRX_MR_RXEN_DISABLE; - /* disable overrun interrupts */ - regmap_write(dev->regmap, SPDIFRX_IDR, - SPDIFRX_IR_OVERRUN); - } + mutex_lock(&dev->mlock); + /* Disable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_OVERRUN); + + /* Disable receiver. */ + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_DISABLE); + dev->trigger_enabled = false; + mutex_unlock(&dev->mlock); break; default: - return -EINVAL; + ret = -EINVAL; } - ret = regmap_write(dev->regmap, SPDIFRX_MR, mr); - if (ret) { - dev_err(dev->dev, "unable to enable/disable RX: %d\n", ret); - return ret; - } - - return 0; + return ret; } static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, @@ -401,7 +429,7 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); - u32 mr; + u32 mr = 0; int ret; dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", @@ -413,13 +441,6 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - regmap_read(dev->regmap, SPDIFRX_MR, &mr); - - if (mr & SPDIFRX_MR_RXEN_ENABLE) { - dev_err(dev->dev, "PCM already running\n"); - return -EBUSY; - } - if (params_channels(params) != SPDIFRX_CHANNELS) { dev_err(dev->dev, "unsupported number of channels: %d\n", params_channels(params)); @@ -445,47 +466,46 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (dev->gclk_enabled) { - clk_disable_unprepare(dev->gclk); - dev->gclk_enabled = 0; + mutex_lock(&dev->mlock); + if (dev->trigger_enabled) { + dev_err(dev->dev, "PCM already running\n"); + ret = -EBUSY; + goto unlock; } + + /* GCLK is enabled by runtime PM. */ + clk_disable_unprepare(dev->gclk); + ret = clk_set_min_rate(dev->gclk, params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1); if (ret) { dev_err(dev->dev, "unable to set gclk min rate: rate %u * ratio %u + 1\n", params_rate(params), SPDIFRX_GCLK_RATIO_MIN); - return ret; + /* Restore runtime PM state. */ + clk_prepare_enable(dev->gclk); + goto unlock; } ret = clk_prepare_enable(dev->gclk); if (ret) { dev_err(dev->dev, "unable to enable gclk: %d\n", ret); - return ret; + goto unlock; } - dev->gclk_enabled = 1; dev_dbg(dev->dev, "GCLK range min set to %d\n", params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1); - return regmap_write(dev->regmap, SPDIFRX_MR, mr); -} + ret = regmap_write(dev->regmap, SPDIFRX_MR, mr); -static int mchp_spdifrx_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); +unlock: + mutex_unlock(&dev->mlock); - if (dev->gclk_enabled) { - clk_disable_unprepare(dev->gclk); - dev->gclk_enabled = 0; - } - return 0; + return ret; } static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { .trigger = mchp_spdifrx_trigger, .hw_params = mchp_spdifrx_hw_params, - .hw_free = mchp_spdifrx_hw_free, }; #define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000 @@ -515,22 +535,58 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, { struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel]; - int ret; - - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); - /* check for new data available */ - ret = wait_for_completion_interruptible_timeout(&ch_stat->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret < 0) { - dev_dbg(dev->dev, "channel status for channel %d timeout\n", - channel); + int ret = 0; + + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion and return with + * timeout check the dev->trigger_enabled. + * + * To retrieve data: + * - if the receiver is enabled CSC IRQ will update the data in software + * caches (ch_stat->data) + * - otherwise we just update it here the software caches with latest + * available information and return it; in this case we don't need + * spin locking as the IRQ is disabled and will not be raised from + * anywhere else. + */ + + if (dev->trigger_enabled) { + reinit_completion(&ch_stat->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); + /* Check for new data available */ + ret = wait_for_completion_interruptible_timeout(&ch_stat->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present */ + if (ret <= 0) { + dev_dbg(dev->dev, "channel status for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel)); + ret = ret ? : -ETIMEDOUT; + goto pm_runtime_put; + } else { + ret = 0; + } + } else { + /* Update software cache with latest channel status. */ + mchp_spdifrx_channel_status_read(dev, channel); } memcpy(uvalue->value.iec958.status, ch_stat->data, sizeof(ch_stat->data)); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol, @@ -564,29 +620,56 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, int channel, struct snd_ctl_elem_value *uvalue) { - unsigned long flags; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel]; - int ret; - - reinit_completion(&user_data->done); - mchp_spdifrx_isr_blockend_en(dev); - ret = wait_for_completion_interruptible_timeout(&user_data->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret <= 0) { - dev_dbg(dev->dev, "user data for channel %d timeout\n", - channel); - mchp_spdifrx_isr_blockend_dis(dev); - return ret; + int ret = 0; + + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion to just timeout we + * check here the dev->trigger_enabled flag. + * + * To retrieve data: + * - if the receiver is enabled we need to wait for blockend IRQ to read + * data to and update it for us in software caches + * - otherwise reading the SPDIFRX_CHUD() registers is enough. + */ + + if (dev->trigger_enabled) { + reinit_completion(&user_data->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); + ret = wait_for_completion_interruptible_timeout(&user_data->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present. */ + if (ret <= 0) { + dev_dbg(dev->dev, "user data for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); + ret = ret ? : -ETIMEDOUT; + goto pm_runtime_put; + } else { + ret = 0; + } + } else { + /* Update software cache with last available data. */ + mchp_spdifrx_channel_user_data_read(dev, channel); } - spin_lock_irqsave(&user_data->lock, flags); memcpy(uvalue->value.iec958.subcode, user_data->data, sizeof(user_data->data)); - spin_unlock_irqrestore(&user_data->lock, flags); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol, @@ -625,12 +708,34 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol, struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; u32 val; + int ret; bool ulock_old = ctrl->ulock; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK); + } else { + ctrl->ulock = 0; + } + uvalue->value.integer.value[0] = ctrl->ulock; + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ulock_old != ctrl->ulock; } @@ -641,10 +746,32 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol, struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; u32 val; + int ret; bool badf_old = ctrl->badf; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->badf = !!(val & SPDIFRX_RSR_BADF); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->badf = !!(val & SPDIFRX_RSR_BADF); + } else { + ctrl->badf = 0; + } + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + uvalue->value.integer.value[0] = ctrl->badf; return badf_old != ctrl->badf; @@ -656,11 +783,49 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol, struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; - u32 val; + u32 val = ~0U, loops = 10; + int ret; bool signal_old = ctrl->signal; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * To get the signal we need to have receiver enabled. This + * could be enabled also from trigger() function thus we need to + * take care of not disabling the receiver when it runs. + */ + if (!dev->trigger_enabled) { + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_ENABLE); + + /* Wait for RSR.ULOCK bit. */ + while (--loops) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + if (!(val & SPDIFRX_RSR_ULOCK)) + break; + usleep_range(100, 150); + } + + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_DISABLE); + } else { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + } + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + +unlock: + mutex_unlock(&dev->mlock); + + if (!(val & SPDIFRX_RSR_ULOCK)) + ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL); + else + ctrl->signal = 0; uvalue->value.integer.value[0] = ctrl->signal; return signal_old != ctrl->signal; @@ -682,22 +847,44 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol, { struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + unsigned long rate; u32 val; - int rate; - - regmap_read(dev->regmap, SPDIFRX_RSR, &val); + int ret; - /* if the receiver is not locked, ISF data is invalid */ - if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) { + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + /* If the receiver is not locked, ISF data is invalid. */ + if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) { + ucontrol->value.integer.value[0] = 0; + goto pm_runtime_put; + } + } else { + /* Reveicer is not locked, IFS data is invalid. */ ucontrol->value.integer.value[0] = 0; - return 0; + goto pm_runtime_put; } rate = clk_get_rate(dev->gclk); ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val)); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = { @@ -787,14 +974,6 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; int ch; - int err; - - err = clk_prepare_enable(dev->pclk); - if (err) { - dev_err(dev->dev, - "failed to enable the peripheral clock: %d\n", err); - return err; - } snd_soc_dai_init_dma_data(dai, NULL, &dev->capture); @@ -808,11 +987,9 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) SPDIFRX_MR_AUTORST_NOACTION | SPDIFRX_MR_PACK_DISABLED); - dev->blockend_refcount = 0; for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { init_completion(&ctrl->ch_stat[ch].done); init_completion(&ctrl->user_data[ch].done); - spin_lock_init(&ctrl->user_data[ch].lock); } /* Add controls */ @@ -827,9 +1004,7 @@ static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai) struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); /* Disable interrupts */ - regmap_write(dev->regmap, SPDIFRX_IDR, 0xFF); - - clk_disable_unprepare(dev->pclk); + regmap_write(dev->regmap, SPDIFRX_IDR, GENMASK(14, 0)); return 0; } @@ -861,6 +1036,48 @@ static const struct of_device_id mchp_spdifrx_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids); +static int mchp_spdifrx_runtime_suspend(struct device *dev) +{ + struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev); + + regcache_cache_only(spdifrx->regmap, true); + clk_disable_unprepare(spdifrx->gclk); + clk_disable_unprepare(spdifrx->pclk); + + return 0; +} + +static int mchp_spdifrx_runtime_resume(struct device *dev) +{ + struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdifrx->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(spdifrx->gclk); + if (ret) + goto disable_pclk; + + regcache_cache_only(spdifrx->regmap, false); + regcache_mark_dirty(spdifrx->regmap); + ret = regcache_sync(spdifrx->regmap); + if (ret) { + regcache_cache_only(spdifrx->regmap, true); + clk_disable_unprepare(spdifrx->gclk); +disable_pclk: + clk_disable_unprepare(spdifrx->pclk); + } + + return ret; +} + +static const struct dev_pm_ops mchp_spdifrx_pm_ops = { + RUNTIME_PM_OPS(mchp_spdifrx_runtime_suspend, mchp_spdifrx_runtime_resume, + NULL) +}; + static int mchp_spdifrx_probe(struct platform_device *pdev) { struct mchp_spdifrx_dev *dev; @@ -913,19 +1130,36 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) "failed to get the PMC generated clock: %d\n", err); return err; } - spin_lock_init(&dev->blockend_lock); + + /* + * Signal control need a valid rate on gclk. hw_params() configures + * it propertly but requesting signal before any hw_params() has been + * called lead to invalid value returned for signal. Thus, configure + * gclk at a valid rate, here, in initialization, to simplify the + * control path. + */ + clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1); + + mutex_init(&dev->mlock); dev->dev = &pdev->dev; dev->regmap = regmap; platform_set_drvdata(pdev, dev); + pm_runtime_enable(dev->dev); + if (!pm_runtime_enabled(dev->dev)) { + err = mchp_spdifrx_runtime_resume(dev->dev); + if (err) + goto pm_runtime_disable; + } + dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR; dev->capture.maxburst = 1; err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (err) { dev_err(&pdev->dev, "failed to register PCM: %d\n", err); - return err; + goto pm_runtime_suspend; } err = devm_snd_soc_register_component(&pdev->dev, @@ -933,20 +1167,40 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) &mchp_spdifrx_dai, 1); if (err) { dev_err(&pdev->dev, "fail to register dai\n"); - return err; + goto pm_runtime_suspend; } regmap_read(regmap, SPDIFRX_VERSION, &vers); dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK); return 0; + +pm_runtime_suspend: + if (!pm_runtime_status_suspended(dev->dev)) + mchp_spdifrx_runtime_suspend(dev->dev); +pm_runtime_disable: + pm_runtime_disable(dev->dev); + return err; +} + +static int mchp_spdifrx_remove(struct platform_device *pdev) +{ + struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev); + + pm_runtime_disable(dev->dev); + if (!pm_runtime_status_suspended(dev->dev)) + mchp_spdifrx_runtime_suspend(dev->dev); + + return 0; } static struct platform_driver mchp_spdifrx_driver = { .probe = mchp_spdifrx_probe, + .remove = mchp_spdifrx_remove, .driver = { .name = "mchp_spdifrx", .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids), + .pm = pm_ptr(&mchp_spdifrx_pm_ops), }, }; diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c index dc96a6fbf514..20d135c718b0 100644 --- a/sound/soc/atmel/mchp-spdiftx.c +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -6,6 +6,7 @@ // // Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> @@ -71,11 +72,11 @@ /* Valid Bits per Sample */ #define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) -#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK) +#define SPDIFTX_MR_VBPS(bps) FIELD_PREP(SPDIFTX_MR_VBPS_MASK, bps) /* Chunk Size */ #define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) -#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK) +#define SPDIFTX_MR_CHUNK(size) FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, size) /* Validity Bits for Channels 1 and 2 */ #define SPDIFTX_MR_VALID1 BIT(24) @@ -88,8 +89,7 @@ /* Bytes per Sample */ #define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) -#define SPDIFTX_MR_BPS(bytes) \ - ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK) +#define SPDIFTX_MR_BPS(bytes) FIELD_PREP(SPDIFTX_MR_BPS_MASK, (bytes - 1)) /* * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 982151330c89..70840f27d4a7 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -202,8 +202,8 @@ static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) info->dma_params_rx.filter_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; - dai->playback_dma_data = &info->dma_params_tx; - dai->capture_dma_data = &info->dma_params_rx; + snd_soc_dai_init_dma_data(dai, &info->dma_params_tx, + &info->dma_params_rx); return 0; } @@ -359,6 +359,8 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, if (dir == SND_SOC_CLOCK_IN || clk_id != 0) return -EINVAL; + if (!freq) + return 0; return clk_set_rate(info->mclk, freq); } diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index fc65283031cd..3574c68e0dda 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1386,17 +1386,11 @@ static int pm860x_codec_probe(struct platform_device *pdev) return ret; } -static int pm860x_codec_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver pm860x_codec_driver = { .driver = { .name = "88pm860x-codec", }, .probe = pm860x_codec_probe, - .remove = pm860x_codec_remove, }; module_platform_driver(pm860x_codec_driver); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0f9d71490075..bd72c426a93d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ALC5623 imply SND_SOC_ALC5632 imply SND_SOC_AW8738 + imply SND_SOC_AW88395 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CQ0093VC @@ -68,6 +69,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L45_I2C imply SND_SOC_CS35L45_SPI imply SND_SOC_CS42L42 + imply SND_SOC_CS42L42_SDW imply SND_SOC_CS42L51_I2C imply SND_SOC_CS42L52 imply SND_SOC_CS42L56 @@ -107,6 +109,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA imply SND_SOC_ICS43432 + imply SND_SOC_IDT821034 imply SND_SOC_INNO_RK3036 imply SND_SOC_ISABELLE imply SND_SOC_JZ4740_CODEC @@ -164,6 +167,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_PCM5102A imply SND_SOC_PCM512x_I2C imply SND_SOC_PCM512x_SPI + imply SND_SOC_PEB2466 imply SND_SOC_RK3328 imply SND_SOC_RK817 imply SND_SOC_RT274 @@ -195,6 +199,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT700_SDW imply SND_SOC_RT711_SDW imply SND_SOC_RT711_SDCA_SDW + imply SND_SOC_RT712_SDCA_SDW imply SND_SOC_RT715_SDW imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW @@ -206,6 +211,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_SI476X imply SND_SOC_SIMPLE_AMPLIFIER imply SND_SOC_SIMPLE_MUX + imply SND_SOC_SMA1303 imply SND_SOC_SPDIF imply SND_SOC_SRC4XXX_I2C imply SND_SOC_SSM2305 @@ -599,6 +605,23 @@ config SND_SOC_AW8738 SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the operation mode using the Awinic-specific one-wire pulse control. +config SND_SOC_AW88395_LIB + tristate + +config SND_SOC_AW88395 + tristate "Soc Audio for awinic aw88395" + depends on I2C + select CRC8 + select CRC32 + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + this option enables support for aw88395 Smart PA. + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10V + smart boost convert. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help @@ -665,9 +688,6 @@ config SND_SOC_CS35L41_I2C select SND_SOC_CS35L41 select REGMAP_I2C -config SND_SOC_CS35L45_TABLES - tristate - config SND_SOC_CS35L45 tristate @@ -676,7 +696,6 @@ config SND_SOC_CS35L45_SPI depends on SPI_MASTER select REGMAP select REGMAP_SPI - select SND_SOC_CS35L45_TABLES select SND_SOC_CS35L45 help Enable support for Cirrus Logic CS35L45 smart speaker amplifier @@ -687,7 +706,6 @@ config SND_SOC_CS35L45_I2C depends on I2C select REGMAP select REGMAP_I2C - select SND_SOC_CS35L45_TABLES select SND_SOC_CS35L45 help Enable support for Cirrus Logic CS35L45 smart speaker amplifier @@ -703,6 +721,13 @@ config SND_SOC_CS42L42 select REGMAP_I2C select SND_SOC_CS42L42_CORE +config SND_SOC_CS42L42_SDW + tristate "Cirrus Logic CS42L42 CODEC on Soundwire" + depends on SOUNDWIRE + select SND_SOC_CS42L42_CORE + help + Enable support for Cirrus Logic CS42L42 codec with Soundwire control + config SND_SOC_CS42L51 tristate @@ -972,6 +997,16 @@ config SND_SOC_HDA config SND_SOC_ICS43432 tristate "ICS43423 and compatible i2s microphones" +config SND_SOC_IDT821034 + tristate "Renesas IDT821034 quad PCM codec" + depends on SPI + help + Enable support for the Renesas IDT821034 quad PCM with + programmable gain codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-idt821034. + config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" select REGMAP_MMIO @@ -1205,6 +1240,17 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_PEB2466 + tristate "Infineon PEB2466 quad PCM codec" + depends on SPI + select REGMAP_SPI + help + Enable support for the Infineon PEB2466 quad PCM codec, + also named SICOFI 4-uC. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-peb2466. + config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO @@ -1425,6 +1471,12 @@ config SND_SOC_RT711_SDCA_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT712_SDCA_SDW + tristate "Realtek RT712 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT715 tristate @@ -1492,6 +1544,12 @@ config SND_SOC_SIMPLE_MUX tristate "Simple Audio Mux" depends on GPIOLIB +config SND_SOC_SMA1303 + tristate "Iron Device SMA1303 Audio Amplifier" + depends on I2C + help + Enable support for Iron Device SMA1303 Boosted Class-D amplifier + config SND_SOC_SPDIF tristate "S/PDIF CODEC" @@ -2111,7 +2169,6 @@ config SND_SOC_MT6660 config SND_SOC_NAU8315 tristate "Nuvoton Technology Corporation NAU8315 CODEC" - depends on GPIOLIB config SND_SOC_NAU8540 tristate "Nuvoton Technology Corporation NAU85L40 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 71d3ce5867e4..f1ca18f7946c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -46,6 +46,9 @@ snd-soc-ak5386-objs := ak5386.o snd-soc-ak5558-objs := ak5558.o snd-soc-arizona-objs := arizona.o arizona-jack.o snd-soc-aw8738-objs := aw8738.o +snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o +snd-soc-aw88395-objs := aw88395/aw88395.o \ + aw88395/aw88395_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-cpcap-objs := cpcap.o @@ -60,12 +63,12 @@ snd-soc-cs35l41-lib-objs := cs35l41-lib.o snd-soc-cs35l41-objs := cs35l41.o snd-soc-cs35l41-spi-objs := cs35l41-spi.o snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o -snd-soc-cs35l45-tables-objs := cs35l45-tables.o -snd-soc-cs35l45-objs := cs35l45.o +snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o snd-soc-cs35l45-spi-objs := cs35l45-spi.o snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o +snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -111,6 +114,7 @@ snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hda-objs := hdac_hda.o snd-soc-hda-codec-objs := hda.o hda-dai.o snd-soc-ics43432-objs := ics43432.o +snd-soc-idt821034-objs := idt821034.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o @@ -183,6 +187,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-peb2466-objs := peb2466.o snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rk817-objs := rk817_codec.o snd-soc-rl6231-objs := rl6231.o @@ -222,6 +227,7 @@ snd-soc-rt5682s-objs := rt5682s.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o +snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o snd-soc-rt9120-objs := rt9120.o @@ -233,6 +239,7 @@ snd-soc-sigmadsp-objs := sigmadsp.o snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o snd-soc-si476x-objs := si476x.o +snd-soc-sma1303-objs := sma1303.o snd-soc-spdif-tx-objs := spdif_transmitter.o snd-soc-spdif-rx-objs := spdif_receiver.o snd-soc-src4xxx-objs := src4xxx.o @@ -407,6 +414,8 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o +obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o +obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o @@ -421,12 +430,12 @@ obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o -obj-$(CONFIG_SND_SOC_CS35L45_TABLES) += snd-soc-cs35l45-tables.o obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o +obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o @@ -472,6 +481,7 @@ obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o +obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o @@ -539,6 +549,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o 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_PEB2466) += snd-soc-peb2466.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 @@ -579,6 +590,7 @@ obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o +obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o @@ -588,6 +600,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o +obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index cc12052e1920..0e013edfe63d 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -127,18 +127,12 @@ static int ac97_probe(struct platform_device *pdev) &soc_component_dev_ac97, &ac97_dai, 1); } -static int ac97_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ac97_codec_driver = { .driver = { .name = "ac97-codec", }, .probe = ac97_probe, - .remove = ac97_remove, }; module_platform_driver(ac97_codec_driver); diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c index 401bafabc8eb..c9134e1de0b2 100644 --- a/sound/soc/codecs/adau7002.c +++ b/sound/soc/codecs/adau7002.c @@ -100,11 +100,6 @@ static int adau7002_probe(struct platform_device *pdev) &adau7002_dai, 1); } -static int adau7002_remove(struct platform_device *pdev) -{ - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id adau7002_dt_ids[] = { { .compatible = "adi,adau7002", }, @@ -128,7 +123,6 @@ static struct platform_driver adau7002_driver = { .acpi_match_table = ACPI_PTR(adau7002_acpi_match), }, .probe = adau7002_probe, - .remove = adau7002_remove, }; module_platform_driver(adau7002_driver); diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c new file mode 100644 index 000000000000..afdce6b7fa26 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.c -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw88395.h" +#include "aw88395_device.h" +#include "aw88395_lib.h" +#include "aw88395_reg.h" + +static const struct regmap_config aw88395_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88395_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88395_start_pa(struct aw88395 *aw88395) +{ + int ret, i; + + for (i = 0; i < AW88395_START_RETRIES; i++) { + ret = aw88395_dev_start(aw88395->aw_pa); + if (ret) { + dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i); + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_info(aw88395->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88395_startup_work(struct work_struct *work) +{ + struct aw88395 *aw88395 = + container_of(work, struct aw88395, start_work.work); + + mutex_lock(&aw88395->lock); + aw88395_start_pa(aw88395); + mutex_unlock(&aw88395->lock); +} + +static void aw88395_start(struct aw88395 *aw88395, bool sync_start) +{ + int ret; + + if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK) + return; + + if (aw88395->aw_pa->status == AW88395_DEV_PW_ON) + return; + + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start == AW88395_SYNC_START) + aw88395_start_pa(aw88395); + else + queue_delayed_work(system_wq, + &aw88395->start_work, + AW88395_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88395_dai[] = { + { + .name = "aw88395-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + }, +}; + +static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88395_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88395_dev_get_profile_count(aw88395->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88395_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa); + + return 0; +} + +static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88395->lock); + ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88395->lock); + return 0; + } + + if (aw88395->aw_pa->status) { + aw88395_dev_stop(aw88395->aw_pa); + aw88395_start(aw88395, AW88395_SYNC_START); + } + + mutex_unlock(&aw88395->lock); + + return 1; +} + +static int aw88395_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88395_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step; + + return 0; +} + +static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88395->aw_pa->fade_step != value) { + aw88395->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static int aw88395_re_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw88395_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw_dev->cali_desc.cali_re != value) { + aw_dev->cali_desc.cali_re = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88395_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG, + 6, AW88395_MUTE_VOL, 0, aw88395_volume_get, + aw88395_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0, + aw88395_get_fade_step, aw88395_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_in_time, aw88395_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_out_time, aw88395_set_fade_out_time), + SOC_SINGLE_EXT("Calib", 0, 0, 100, 0, + aw88395_re_get, aw88395_re_set), + AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info, + aw88395_profile_get, aw88395_profile_set), +}; + +static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88395->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88395_start(aw88395, AW88395_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88395_dev_stop(aw88395->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88395->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88395_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88395_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88395_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets, + ARRAY_SIZE(aw88395_dapm_widgets)); + if (ret < 0) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map, + ARRAY_SIZE(aw88395_audio_map)); + if (ret < 0) + return ret; + + ret = snd_soc_add_component_controls(component, aw88395_controls, + ARRAY_SIZE(aw88395_controls)); + + return ret; +} + +static void aw88395_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88395->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88395 = { + .probe = aw88395_codec_probe, + .remove = aw88395_codec_remove, +}; + +static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c) +{ + struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev, + sizeof(struct aw88395), GFP_KERNEL); + if (!aw88395) + return NULL; + + mutex_init(&aw88395->lock); + + return aw88395; +} + +static void aw88395_hw_reset(struct aw88395 *aw88395) +{ + if (aw88395->reset_gpio) { + gpiod_set_value_cansleep(aw88395->reset_gpio, 0); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + gpiod_set_value_cansleep(aw88395->reset_gpio, 1); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + } else { + dev_err(aw88395->aw_pa->dev, "%s failed", __func__); + } +} + +static int aw88395_request_firmware_file(struct aw88395 *aw88395) +{ + const struct firmware *cont = NULL; + int ret; + + aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev); + if ((ret < 0) || (!cont)) { + dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE); + return ret; + } + + dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n", + AW88395_ACF_FILE, cont ? cont->size : 0); + + aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88395->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88395->aw_cfg->len = (int)cont->size; + memcpy(aw88395->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE); + return ret; + } + + dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); + + mutex_lock(&aw88395->lock); + /* aw device init */ + ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) + dev_err(aw88395->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88395->lock); + + return ret; +} + +static int aw88395_i2c_probe(struct i2c_client *i2c) +{ + struct aw88395 *aw88395; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + aw88395 = aw88395_malloc_init(i2c); + if (!aw88395) { + dev_err(&i2c->dev, "malloc aw88395 failed"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, aw88395); + + aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88395->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + + /* hardware reset */ + aw88395_hw_reset(aw88395); + + aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config); + if (IS_ERR(aw88395->regmap)) { + ret = PTR_ERR(aw88395->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + /* aw pa init */ + ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap); + if (ret < 0) + return ret; + + ret = aw88395_request_firmware_file(aw88395); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed\n", __func__); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88395, + aw88395_dai, ARRAY_SIZE(aw88395_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "failed to register aw88395: %d", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id aw88395_i2c_id[] = { + { AW88395_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); + +static struct i2c_driver aw88395_i2c_driver = { + .driver = { + .name = AW88395_I2C_NAME, + }, + .probe_new = aw88395_i2c_probe, + .id_table = aw88395_i2c_id, +}; +module_i2c_driver(aw88395_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h new file mode 100644 index 000000000000..8036ba27f68d --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.h -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#ifndef __AW88395_H__ +#define __AW88395_H__ + +#define AW88395_CHIP_ID_REG (0x00) +#define AW88395_START_RETRIES (5) +#define AW88395_START_WORK_DELAY_MS (0) + +#define AW88395_DSP_16_DATA_MASK (0x0000ffff) + +#define AW88395_I2C_NAME "aw88395_smartpa" + +#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88395_SYNC_START = 0, + AW88395_ASYNC_START, +}; + +enum { + AW88395_STREAM_CLOSE = 0, + AW88395_STREAM_OPEN, +}; + +struct aw88395 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h new file mode 100644 index 000000000000..e7aa56178b36 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_data_type.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883_data_type.h -- The data type of the AW88395 chip +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#ifndef __AW88395_DATA_TYPE_H__ +#define __AW88395_DATA_TYPE_H__ + +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) + +#define ACF_FILE_ID (0xa15f908) + +enum aw_cfg_hdr_version { + AW88395_CFG_HDR_VER = 0x00000001, + AW88395_CFG_HDR_VER_V1 = 0x01000000, +}; + +enum aw_cfg_dde_type { + AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF, + AW88395_DEV_TYPE_ID = 0x00000000, + AW88395_SKT_TYPE_ID = 0x00000001, + AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MULTIPLE_BIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW88395_DATA_TYPE_REG = 0, + AW88395_DATA_TYPE_DSP_CFG, + AW88395_DATA_TYPE_DSP_FW, + AW88395_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW88395_PROFILE_MUSIC = 0, + AW88395_PROFILE_VOICE, + AW88395_PROFILE_VOIP, + AW88395_PROFILE_RINGTONE, + AW88395_PROFILE_RINGTONE_HS, + AW88395_PROFILE_LOWPOWER, + AW88395_PROFILE_BYPASS, + AW88395_PROFILE_MMI, + AW88395_PROFILE_FM, + AW88395_PROFILE_NOTIFICATION, + AW88395_PROFILE_RECEIVER, + AW88395_PROFILE_MAX, +}; + +enum aw_profile_status { + AW88395_PROFILE_WAIT = 0, + AW88395_PROFILE_OK, +}; + +struct aw_cfg_hdr { + u32 id; + char project[PROJECT_NAME_MAX]; + char custom[CUSTOMER_NAME_MAX]; + char version[CFG_VERSION_MAX]; + u32 author_id; + u32 ddt_size; + u32 ddt_num; + u32 hdr_offset; + u32 hdr_version; + u32 reserved[3]; +}; + +struct aw_cfg_dde { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + u32 reserved[5]; +}; + +struct aw_cfg_dde_v1 { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + u32 chip_id; + u32 reserved[4]; +}; + +struct aw_sec_data_desc { + u32 len; + u8 *data; +}; + +struct aw_prof_desc { + u32 id; + u32 prof_st; + char *prf_str; + u32 fw_ver; + struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int prof_type; + char **prof_name_list; + struct aw_prof_desc *prof_desc; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c new file mode 100644 index 000000000000..33eda3741464 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -0,0 +1,1748 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.c -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// Author: Ben Yi <yijiangtao@awinic.com> +// + +#include <linux/crc32.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include "aw88395_device.h" +#include "aw88395_reg.h" + +static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + u16 temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW88395_DSP_16_DATA_MASK; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + +static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + return 0; +} + +static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data |= (temp_data << 16); + + return 0; +} + +static int aw_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + + +static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id) +{ + int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + + dev_info(aw_dev->dev, "chip id = %x\n", reg_val); + *chip_id = reg_val; + + return 0; +} + +static unsigned int reg_val_to_db(unsigned int value) +{ + return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) + + ((value & 0x3f) % AW88395_VOLUME_STEP_DB)); +} + +static unsigned short db_to_reg_val(unsigned short value) +{ + return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) + + (value % AW88395_VOLUME_STEP_DB)); +} + +static int aw_dev_dsp_fw_check(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_fw_desc; + struct aw_prof_desc *set_prof_desc; + u16 base_addr = AW88395_DSP_FW_ADDR; + u16 addr = base_addr; + u32 dsp_val; + u16 bin_val; + int ret, i; + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc); + if (ret) + return ret; + + /* update reg */ + dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW]; + + for (i = 0; i < AW88395_FW_CHECK_PART; i++) { + ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "dsp read failed"); + return ret; + } + + bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]); + + if (dsp_val != bin_val) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]", + addr, dsp_val, bin_val); + return -EINVAL; + } + + addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART; + if ((addr - base_addr) > dsp_fw_desc->len) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value, volume; + int ret; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL); + real_value = db_to_reg_val(volume); + + /* cal real value */ + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK); + + /* write value */ + ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value); + + return ret; +} + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol) +{ + int ret; + + ret = aw_dev_set_volume(aw_dev, set_vol); + if (ret) + dev_dbg(aw_dev->dev, "set volume failed"); +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88395_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + unsigned int addr_offset; + __le16 data1; + __le32 data2; + + dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data); + + addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2; + if (addr_offset > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]", + addr_offset, crc_dsp_cfg->len); + return -EINVAL; + } + switch (data_type) { + case AW88395_DSP_16_DATA: + data1 = cpu_to_le16((u16)dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2); + break; + case AW88395_DSP_32_DATA: + data2 = cpu_to_le32(dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev) +{ + u32 cali_re; + int ret; + + cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT); + + /* set cali re to device */ + ret = aw_dev_dsp_write(aw_dev, + AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE, + cali_re, AW88395_DSP_32_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + u32 crc_value, crc_data_len; + + /* get crc data len */ + crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2; + if (crc_data_len > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d", + crc_data_len, crc_dsp_cfg->len); + return -EINVAL; + } + + if (crc_data_len & 0x11) { + dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len); + return -EINVAL; + } + + crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF; + + return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value, + AW88395_DSP_32_DATA); +} + +static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_check_st(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + int i; + + for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG); + continue; + } + + if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) { + dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val); + ret = -EPERM; + continue; + } else { + dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val); + return 0; + } + } + + return ret; +} + +static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "enable dsp failed"); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "disable dsp failed"); + } +} + +static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) { + dev_info(aw_dev->dev, "dsp bypass"); + return 0; + } + + ret = aw_dev_dsp_set_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "set dsp crc32 failed"); + return ret; + } + + aw_dev_dsp_check_crc_enable(aw_dev, true); + + /* dsp enable */ + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_5000_US, AW88395_5000_US + 100); + + ret = aw_dev_dsp_check_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check crc32 fail"); + } else { + aw_dev_dsp_check_crc_enable(aw_dev, false); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK; + } + + return ret; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + int ret; + + if (is_mute) { + aw_dev_fade_out(aw_dev); + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} +EXPORT_SYMBOL_GPL(aw88395_dev_mute); + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + unsigned int reg_val; + u16 reg_icalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK); + + if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, ®_val); + if (ret) + return ret; + + reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT; + + reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK); + + if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK)) + reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT; + + if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select) +{ + unsigned int vsense_reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val); + if (ret) { + dev_err(aw_dev->dev, "read vsense_reg_val failed"); + return ret; + } + dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val); + + if (vsense_reg_val & (~AW88395_VDSEL_MASK)) { + *vsense_select = AW88395_DEV_VDSEL_VSENSE; + dev_dbg(aw_dev->dev, "vsense outside"); + } else { + *vsense_select = AW88395_DEV_VDSEL_DAC; + dev_dbg(aw_dev->dev, "vsense inside"); + } + + return 0; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vsense_select; + u32 vcalb_adj, reg_val; + int vcalb, vcalk; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "read vcalb_adj failed"); + return ret; + } + + ret = aw_dev_vsense_select(aw_dev, &vsense_select); + if (ret) + return ret; + dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select); + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val; + + switch (vsense_select) { + case AW88395_DEV_VDSEL_VSENSE: + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk); + break; + case AW88395_DEV_VDSEL_DAC: + ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR_DAC, + AW88395_VSCAL_FACTOR_DAC, icalk, vcalk); + break; + default: + dev_err(aw_dev->dev, "unsupport vsense status"); + return -EINVAL; + } + + if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) { + dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported", + vcalk, AW88395_ISCAL_FACTOR); + return -EINVAL; + } + + vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR; + reg_val = (u32)vcalb; + + dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x", + vcalb, reg_val, vcalb_adj); + + ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "write vcalb failed"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB, + (u32)reg_val, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev) +{ + struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc; + u32 cali_delay; + int ret; + + ret = aw_dev_dsp_read(aw_dev, + AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret); + else + desc->delay = AW88395_CALI_DELAY_CACL(cali_delay); + + dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay); + + return ret; +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return -EIO; + if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88395_CCO_MUX_MASK); + if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK) + & AW88395_BIT_SYSST_CHECK; + if (check_val != AW88395_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88395_BIT_SYSST_CHECK); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + + if (reg_val & AW88395_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88395_RCV_MODE; + else + profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE; +} + +static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg) +{ + unsigned int reg_val = 0; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if (reg_val & (~AW88395_DSPBY_MASK)) + *dsp_cfg = AW88395_DEV_DSP_BYPASS; + else + *dsp_cfg = AW88395_DEV_DSP_WORK; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88395_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_DAP_HCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88395_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_OSC_CLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88395_WDT_CNT_MASK))) + ret = -EPERM; + + return ret; +} + +static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax) +{ + return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW88395_DSP_16_DATA); +} + +static int aw_dev_update_reg_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val; + int16_t *reg_data; + int data_len; + u16 read_vol; + u16 reg_val; + u8 reg_addr; + int i, ret; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88395_SYSCTRL_REG) { + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + read_val &= (~AW88395_HMUTE_MASK); + reg_val &= AW88395_HMUTE_MASK; + reg_val |= read_val; + } + if (reg_addr == AW88395_HAGCCFG7_REG) + reg_val &= AW88395_AGC_DSP_CTL_MASK; + + if (reg_addr == AW88395_I2SCFG1_REG) { + /* close tx */ + reg_val &= AW88395_I2STXEN_MASK; + reg_val |= AW88395_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88395_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88395_VOL_MASK)) >> + AW88395_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + reg_val_to_db(read_vol); + } + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + + } + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) { + /* clear control volume when PA change profile */ + vol_desc->ctl_volume = 0; + } else { + /* keep control volume when PA start with sync mode */ + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + } + + /* keep min volume */ + if (aw_dev->fade_en) + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + + aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg); + + return ret; +} + +static int aw_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw_dev, data, len); + if (ret) { + dev_err(aw_dev->dev, "reg update failed"); + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88395_DSP_RE_SHIFT); + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + int i, ret; + +#ifdef AW88395_DSP_I2C_WRITES + u32 tmp_len; + + mutex_lock(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + + for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len = len - i; + else + tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE; + + ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#else + __be16 reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* i2c write */ + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + for (i = 0; i < len; i += 2) { + reg_val = cpu_to_be16p((u16 *)(data + i)); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + (u16)reg_val); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#endif + + return 0; + +error_operation: + mutex_unlock(&aw_dev->dsp_lock); + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR); + aw_dev->dsp_fw_len = len; + + return 0; +} + +static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int size) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + + if (!crc_dsp_cfg->data) { + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } else if (crc_dsp_cfg->len < size) { + devm_kfree(aw_dev->dev, crc_dsp_cfg->data); + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } + memcpy(crc_dsp_cfg->data, data, size); + swab16_array((u16 *)crc_dsp_cfg->data, size >> 1); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR); + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len); + if (ret) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret) + return ret; + ret = aw_dev_get_ra(&aw_dev->cali_desc); + if (ret) + return ret; + ret = aw_dev_get_cali_f0_delay(aw_dev); + if (ret) + return ret; + + ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax); + if (ret) { + dev_err(aw_dev->dev, "get vmax failed"); + return ret; + } + dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA; + + return 0; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + /* check the even bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_fw_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + mutex_unlock(&aw_dev->dsp_lock); + + return 0; + +error: + mutex_unlock(&aw_dev->dsp_lock); + return -EPERM; +} + +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88395_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index); + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw88395_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + + if (up_dsp_fw_en) { + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + /* update dsp firmware */ + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_fw_update); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88395_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88395_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + int ret; + + if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) && + (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) { + + ret = aw_dev_dsp_set_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "set cali re failed"); + } +} + +int aw88395_dev_start(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status == AW88395_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) { + /* dsp bypass */ + aw_dev_dsp_enable(aw_dev, false); + ret = aw_dev_dsp_fw_check(aw_dev); + if (ret) + goto dev_dsp_fw_check_fail; + + aw_dev_update_cali_re(&aw_dev->cali_desc); + + if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) { + ret = aw_dev_dsp_check_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + } + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + /* close mute */ + aw88395_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88395_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +dev_dsp_fw_check_fail: +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88395_DEV_PW_OFF; + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_start); + +int aw88395_dev_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st = 0; + int ret; + + if (aw_dev->status == AW88395_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88395_DEV_PW_OFF; + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* check sysint state */ + int_st = aw_dev_check_sysint(aw_dev); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + if (int_st < 0) { + /* system status anomaly */ + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + if (ret) + dev_err(aw_dev->dev, "update dsp fw failed"); + ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + if (ret) + dev_err(aw_dev->dev, "update dsp cfg failed"); + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + } + + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_stop); + +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + int ret; + + if ((!aw_dev) || (!aw_cfg)) { + pr_err("aw_dev is NULL or aw_cfg is NULL"); + return -ENOMEM; + } + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88395_1000_US / 10; + aw_dev->fade_out_time = AW88395_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_init); + +static void aw88395_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + int ret; + + ret = of_property_read_u32(np, "sound-channel", &channel_value); + if (ret) { + dev_dbg(aw_dev->dev, + "read sound-channel failed,use default 0"); + aw_dev->channel = AW88395_DEV_DEFAULT_CH; + return; + } + + dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + channel_value); + aw_dev->channel = channel_value; +} + +static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 fade_en; + int ret; + + ret = of_property_read_u32(np, "fade-enable", &fade_en); + if (ret) { + dev_dbg(aw_dev->dev, + "read fade-enable failed, close fade_in_out"); + fade_en = AW88395_FADE_IN_OUT_DEFAULT; + } + + dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); + + aw_dev->fade_en = fade_en; +} + +static int aw_dev_init(struct aw_device *aw_dev) +{ + aw_dev->chip_id = AW88395_CHIP_ID; + /* call aw device init func */ + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88395_DEV_FW_FAILED; + + aw_dev->fade_step = AW88395_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE; + aw88395_parse_channel_dt(aw_dev); + aw88395_parse_fade_enable_dt(aw_dev); + + return 0; +} + +int aw88395_dev_get_profile_count(struct aw_device *aw_dev) +{ + return aw_dev->prof_info.count; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count); + +int aw88395_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->prof_index; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index); + +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EINVAL; + + aw_dev->prof_index = index; + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index); + +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name); + +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data); + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap) +{ + u16 chip_id; + int ret; + + if (*aw_dev) { + dev_info(&i2c->dev, "it should be initialized here.\n"); + } else { + *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL); + if (!(*aw_dev)) + return -ENOMEM; + } + + (*aw_dev)->i2c = i2c; + (*aw_dev)->dev = &i2c->dev; + (*aw_dev)->regmap = regmap; + mutex_init(&(*aw_dev)->dsp_lock); + + /* read chip id */ + ret = aw_dev_read_chipid((*aw_dev), &chip_id); + if (ret) { + dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret); + return ret; + } + + switch (chip_id) { + case AW88395_CHIP_ID: + ret = aw_dev_init((*aw_dev)); + break; + default: + ret = -EINVAL; + dev_err((*aw_dev)->dev, "unsupported device"); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_init); + +MODULE_DESCRIPTION("AW88395 device lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h new file mode 100644 index 000000000000..caf730753167 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.h -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#ifndef __AW88395_DEVICE_FILE_H__ +#define __AW88395_DEVICE_FILE_H__ + +#include "aw88395.h" +#include "aw88395_data_type.h" +#include "aw88395_lib.h" + +#define AW88395_DEV_DEFAULT_CH (0) +#define AW88395_DEV_DSP_CHECK_MAX (5) +#define AW88395_DSP_I2C_WRITES +#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128) +#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555) +#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA) +#define AW88395_DSP_ST_CHECK_MAX (2) +#define AW88395_FADE_IN_OUT_DEFAULT (0) +#define AW88395_CALI_RE_MAX (15000) +#define AW88395_CALI_RE_MIN (4000) +#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48) + +#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift)) +#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) + +#define AW88395_ACF_FILE "aw88395_acf.bin" +#define AW88395_DEV_SYSST_CHECK_MAX (10) + +enum { + AW88395_DEV_VDSEL_DAC = 0, + AW88395_DEV_VDSEL_VSENSE = 1, +}; + +enum { + AW88395_DSP_CRC_NA = 0, + AW88395_DSP_CRC_OK = 1, +}; + +enum { + AW88395_DSP_FW_UPDATE_OFF = 0, + AW88395_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88395_FORCE_UPDATE_OFF = 0, + AW88395_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88395_1000_US = 1000, + AW88395_2000_US = 2000, + AW88395_3000_US = 3000, + AW88395_4000_US = 4000, + AW88395_5000_US = 5000, + AW88395_10000_US = 10000, + AW88395_100000_US = 100000, +}; + +enum { + AW88395_DEV_TYPE_OK = 0, + AW88395_DEV_TYPE_NONE = 1, +}; + + +enum AW88395_DEV_STATUS { + AW88395_DEV_PW_OFF = 0, + AW88395_DEV_PW_ON, +}; + +enum AW88395_DEV_FW_STATUS { + AW88395_DEV_FW_FAILED = 0, + AW88395_DEV_FW_OK, +}; + +enum AW88395_DEV_MEMCLK { + AW88395_DEV_MEMCLK_OSC = 0, + AW88395_DEV_MEMCLK_PLL = 1, +}; + +enum AW88395_DEV_DSP_CFG { + AW88395_DEV_DSP_WORK = 0, + AW88395_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88395_DSP_16_DATA = 0, + AW88395_DSP_32_DATA = 1, +}; + +enum { + AW88395_NOT_RCV_MODE = 0, + AW88395_RCV_MODE = 1, +}; + +struct aw_profctrl_desc { + unsigned int cur_mode; +}; + +struct aw_volume_desc { + unsigned int init_volume; + unsigned int mute_volume; + unsigned int ctl_volume; + unsigned int max_volume; +}; + +struct aw_dsp_mem_desc { + unsigned int dsp_madd_reg; + unsigned int dsp_mdat_reg; + unsigned int dsp_fw_base_addr; + unsigned int dsp_cfg_base_addr; +}; + +struct aw_vmax_desc { + unsigned int init_vmax; +}; + +struct aw_cali_delay_desc { + unsigned int delay; +}; + +struct aw_cali_desc { + u32 cali_re; + u32 ra; +}; + +struct aw_container { + int len; + u8 data[]; +}; + +struct aw_device { + int status; + struct mutex dsp_lock; + + unsigned char prof_cur; + unsigned char prof_index; + unsigned char dsp_crc_st; + u16 chip_id; + + unsigned int channel; + unsigned int fade_step; + + struct i2c_client *i2c; + struct device *dev; + struct regmap *regmap; + char *acf; + + u32 fade_en; + unsigned char dsp_cfg; + + u32 dsp_fw_len; + u32 dsp_cfg_len; + u8 platform; + u8 fw_status; + + unsigned int fade_in_time; + unsigned int fade_out_time; + + struct aw_prof_info prof_info; + struct aw_sec_data_desc crc_dsp_cfg; + struct aw_profctrl_desc profctrl_desc; + struct aw_volume_desc volume_desc; + struct aw_dsp_mem_desc dsp_mem_desc; + struct aw_vmax_desc vmax_desc; + + struct aw_cali_delay_desc cali_delay_desc; + struct aw_cali_desc cali_desc; + +}; + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap); +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_start(struct aw_device *aw_dev); +int aw88395_dev_stop(struct aw_device *aw_dev); +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en); + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol); +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index); +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index); +int aw88395_dev_get_profile_index(struct aw_device *aw_dev); +int aw88395_dev_get_profile_count(struct aw_device *aw_dev); +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg); +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute); + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c new file mode 100644 index 000000000000..05bcf49da857 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.c -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#include <linux/crc8.h> +#include <linux/i2c.h> +#include "aw88395_lib.h" +#include "aw88395_device.h" + +#define AW88395_CRC8_POLYNOMIAL 0x8C +DECLARE_CRC8_TABLE(aw_crc8_table); + +static char *profile_name[AW88395_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", + "Ringtone_hs", "Lowpower", "Bypass", + "Mmi", "Fm", "Notification", "Receiver" +}; + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin); + +static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + unsigned char *p_check_sum; + unsigned int sum_data = 0; + unsigned int check_sum; + unsigned int i, len; + + p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len; + check_sum = le32_to_cpup((void *)p_check_sum); + + for (i = 4; i < len; i++) + sum_data += *(p_check_sum + i); + + dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x", + __func__, p_check_sum, check_sum, sum_data); + if (sum_data != check_sum) { + dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x", + __func__, bin_num, check_sum, sum_data); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 || + bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) { + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_register_num, parse_register_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_register_num = le32_to_cpup((void *)p_check_sum); + check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_dsp_reg_num, parse_dsp_reg_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM)); + bin->header_info[bin_num].reg_data_byte_len = + le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN)); + check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) / + bin->header_info[bin_num].reg_data_byte_len; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n"); + dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, check_dsp_reg_num, check_dsp_reg_num); + return -EINVAL; + } + + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + DSP_VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_soc_app_num, parse_soc_app_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum); + parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, parse_soc_app_num, check_soc_app_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum + + APP_DOWNLOAD_ADDR)); + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + APP_VALID_DATA_ADDR; + + return 0; +} + +static void aw_get_single_bin_header(struct aw_bin *bin) +{ + memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN); + + bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num, + int bin_serial_num, struct aw_bin *bin) +{ + struct bin_header_info aw_bin_header_info; + unsigned int bin_start_addr; + unsigned int valid_data_len; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1]; + if (!bin_serial_num) { + bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET)); + bin->p_addr += (HEADER_LEN + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num + + VALID_DATA_ADDR_OFFSET; + } else { + valid_data_len = aw_bin_header_info.bin_data_len; + bin->p_addr += (HDADER_LEN + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len + + VALID_DATA_ADDR_OFFSET; + } + + return aw_parse_bin_header(aw_dev, bin); +} + +static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_num, i; + int ret; + + bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET)); + if (bin->multi_bin_parse_num == 1) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + + aw_get_single_bin_header(bin); + + for (i = 0; i < bin_num; i++) { + dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_data_type; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + bin->single_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + aw_get_single_bin_header(bin); + return 0; + case DATA_TYPE_MULTI_BINS: + bin->multi_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + return aw_get_multi_bin_header(aw_dev, bin); + default: + dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__); + return 0; + } +} + +static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int header_version; + + header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version); + + switch (header_version) { + case HEADER_VERSION_V1: + return aw_parse_bin_header(aw_dev, bin); + default: + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n"); + return -EINVAL; + } +} + +static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin) +{ + int ret = -EINVAL; + int i; + + if (!bin) { + dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n"); + return ret; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + ret = aw_check_bin_header_version(aw_dev, bin); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n"); + return ret; + } + + for (i = 0; i < bin->all_bin_parse_num; i++) { + ret = aw_check_sum(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n"); + return ret; + } + ret = aw_check_data_version(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check data version error\n"); + return ret; + } + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + switch (bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + ret = aw_check_register_num(aw_dev, bin, i); + break; + case DATA_TYPE_DSP_REG: + ret = aw_check_dsp_reg_num(aw_dev, bin, i); + break; + case DATA_TYPE_SOC_APP: + ret = aw_check_soc_app_num(aw_dev, bin, i); + break; + default: + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + ret = 0; + break; + } + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, + unsigned int data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + int i; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + for (i = 0; i < aw_bin->all_bin_parse_num; i++) { + switch (aw_bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_REG: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_FW: + case DATA_TYPE_SOC_APP: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->fw_ver = aw_bin->header_info[i].app_version; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + default: + dev_dbg(aw_dev->dev, "bin_data_type not found"); + break; + } + } + prof_desc->prof_st = AW88395_PROFILE_OK; + ret = 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) +{ + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_CFG: + return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_FW: + return aw_dev_parse_raw_dsp_fw( + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_MULTIPLE_BIN: + return aw_dev_prof_parse_multi_bin( + aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + default: + dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW88395_DEV_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW88395_DEV_TYPE_NONE; + } + + return AW88395_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_sec_data_desc *sec_desc; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) + prof_info->count++; + } + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + } + + return 0; +} + +static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_all_prof_info *all_prof_info; + int ret; + + all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + if (!all_prof_info) + return -ENOMEM; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + goto exit; + } else if (ret == AW88395_DEV_TYPE_NONE) { + dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + goto exit; + } + + ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + + aw_dev->prof_info.prof_name_list = profile_name; + +exit: + devm_kfree(aw_dev->dev, all_prof_info); + return ret; +} + +static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc = prof_info->prof_desc; + int i; + + if (!prof_desc) { + dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (!prof_info->prof_name_list) + return -ENOMEM; + + for (i = 0; i < prof_info->count; i++) { + prof_desc[i].id = i; + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + int default_num = 0; + int dev_num = 0; + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + if (cfg_dde[i].type == AW88395_DEV_TYPE_ID) + dev_num++; + + if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (dev_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID; + } else if (default_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID; + } else { + dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + return 0; +} + +static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + + return 0; +} + +static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + + return 0; +} + +static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *count) +{ + int ret; + + ret = aw_get_dde_type_info(aw_dev, aw_cfg); + if (ret < 0) + return ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count); + break; + default: + dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, + struct aw_cfg_dde_v1 *cfg_dde, + int *cur_scene_id) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_MULTIPLE_BIN: + ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse multi bin failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; + default: + dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id = 0; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id = 0; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *cfg_hdr) +{ + int ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr); + break; + default: + dev_err(aw_dev->dev, "prof type matched failed, get num[%d]", + aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "parse hdr failed"); + return ret; + } + + ret = aw_dev_create_prof_name_list_v1(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + int ret; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + case AW88395_CFG_HDR_VER_V1: + ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + aw_dev->fw_status = AW88395_DEV_FW_OK; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load); + +static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + struct aw_cfg_dde *cfg_dde; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + unsigned int i; + u8 act_crc8; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_dde_v1 *cfg_dde; + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + u8 act_crc8; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + + if (!aw_cfg) { + dev_err(aw_dev->dev, "aw_prof is NULL"); + return -EINVAL; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg); + case AW88395_CFG_HDR_VER_V1: + return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg); + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check); + +MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h new file mode 100644 index 000000000000..8a620920d8bd --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.h -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#ifndef __AW88395_LIB_H__ +#define __AW88395_LIB_H__ + +#define CHECK_REGISTER_NUM_OFFSET (4) +#define VALID_DATA_LEN (4) +#define VALID_DATA_ADDR (4) +#define PARSE_DSP_REG_NUM (4) +#define REG_DATA_BYTP_LEN (8) +#define CHECK_DSP_REG_NUM (12) +#define DSP_VALID_DATA_LEN (12) +#define DSP_VALID_DATA_ADDR (12) +#define PARSE_SOC_APP_NUM (8) +#define CHECK_SOC_APP_NUM (12) +#define APP_DOWNLOAD_ADDR (4) +#define APP_VALID_DATA_LEN (12) +#define APP_VALID_DATA_ADDR (12) +#define BIN_NUM_MAX (100) +#define HEADER_LEN (60) +#define BIN_DATA_TYPE_OFFSET (8) +#define DATA_LEN (44) +#define VALID_DATA_ADDR_OFFSET (60) +#define START_ADDR_OFFSET (64) + +#define AW88395_FW_CHECK_PART (10) +#define HDADER_LEN (60) + +#define HEADER_VERSION_OFFSET (4) + +enum bin_header_version_enum { + HEADER_VERSION_V1 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_DSP_FW = 0x00000022, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0x00000001, + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int check_sum; + unsigned int header_ver; + unsigned int bin_data_type; + unsigned int bin_data_ver; + unsigned int bin_data_len; + unsigned int ui_ver; + unsigned char chip_type[8]; + unsigned int reg_byte_len; + unsigned int data_byte_len; + unsigned int device_addr; + unsigned int valid_data_len; + unsigned int valid_data_addr; + + unsigned int reg_num; + unsigned int reg_data_byte_len; + unsigned int download_addr; + unsigned int app_version; + unsigned int header_len; +}; + +struct bin_container { + unsigned int len; + unsigned char data[]; +}; + +struct aw_bin { + unsigned char *p_addr; + unsigned int all_bin_parse_num; + unsigned int multi_bin_parse_num; + unsigned int single_bin_parse_num; + struct bin_header_info header_info[BIN_NUM_MAX]; + struct bin_container info; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h new file mode 100644 index 000000000000..e64f24e97150 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_reg.h -- AW88395 chip register file +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <zhaolei@awinic.com> +// + +#ifndef __AW88395_REG_H__ +#define __AW88395_REG_H__ + +#define AW88395_ID_REG (0x00) +#define AW88395_SYSST_REG (0x01) +#define AW88395_SYSINT_REG (0x02) +#define AW88395_SYSINTM_REG (0x03) +#define AW88395_SYSCTRL_REG (0x04) +#define AW88395_SYSCTRL2_REG (0x05) +#define AW88395_I2SCTRL_REG (0x06) +#define AW88395_I2SCFG1_REG (0x07) +#define AW88395_I2SCFG2_REG (0x08) +#define AW88395_HAGCCFG1_REG (0x09) +#define AW88395_HAGCCFG2_REG (0x0A) +#define AW88395_HAGCCFG3_REG (0x0B) +#define AW88395_HAGCCFG4_REG (0x0C) +#define AW88395_HAGCCFG5_REG (0x0D) +#define AW88395_HAGCCFG6_REG (0x0E) +#define AW88395_HAGCCFG7_REG (0x0F) +#define AW88395_MPDCFG_REG (0x10) +#define AW88395_PWMCTRL_REG (0x11) +#define AW88395_I2SCFG3_REG (0x12) +#define AW88395_DBGCTRL_REG (0x13) +#define AW88395_HAGCST_REG (0x20) +#define AW88395_VBAT_REG (0x21) +#define AW88395_TEMP_REG (0x22) +#define AW88395_PVDD_REG (0x23) +#define AW88395_ISNDAT_REG (0x24) +#define AW88395_VSNDAT_REG (0x25) +#define AW88395_I2SINT_REG (0x26) +#define AW88395_I2SCAPCNT_REG (0x27) +#define AW88395_ANASTA1_REG (0x28) +#define AW88395_ANASTA2_REG (0x29) +#define AW88395_ANASTA3_REG (0x2A) +#define AW88395_ANASTA4_REG (0x2B) +#define AW88395_TESTDET_REG (0x2C) +#define AW88395_TESTIN_REG (0x38) +#define AW88395_TESTOUT_REG (0x39) +#define AW88395_DSPMADD_REG (0x40) +#define AW88395_DSPMDAT_REG (0x41) +#define AW88395_WDT_REG (0x42) +#define AW88395_ACR1_REG (0x43) +#define AW88395_ACR2_REG (0x44) +#define AW88395_ASR1_REG (0x45) +#define AW88395_ASR2_REG (0x46) +#define AW88395_DSPCFG_REG (0x47) +#define AW88395_ASR3_REG (0x48) +#define AW88395_ASR4_REG (0x49) +#define AW88395_VSNCTRL1_REG (0x50) +#define AW88395_ISNCTRL1_REG (0x51) +#define AW88395_PLLCTRL1_REG (0x52) +#define AW88395_PLLCTRL2_REG (0x53) +#define AW88395_PLLCTRL3_REG (0x54) +#define AW88395_CDACTRL1_REG (0x55) +#define AW88395_CDACTRL2_REG (0x56) +#define AW88395_SADCCTRL1_REG (0x57) +#define AW88395_SADCCTRL2_REG (0x58) +#define AW88395_CPCTRL1_REG (0x59) +#define AW88395_BSTCTRL1_REG (0x60) +#define AW88395_BSTCTRL2_REG (0x61) +#define AW88395_BSTCTRL3_REG (0x62) +#define AW88395_BSTCTRL4_REG (0x63) +#define AW88395_BSTCTRL5_REG (0x64) +#define AW88395_BSTCTRL6_REG (0x65) +#define AW88395_BSTCTRL7_REG (0x66) +#define AW88395_DSMCFG1_REG (0x67) +#define AW88395_DSMCFG2_REG (0x68) +#define AW88395_DSMCFG3_REG (0x69) +#define AW88395_DSMCFG4_REG (0x6A) +#define AW88395_DSMCFG5_REG (0x6B) +#define AW88395_DSMCFG6_REG (0x6C) +#define AW88395_DSMCFG7_REG (0x6D) +#define AW88395_DSMCFG8_REG (0x6E) +#define AW88395_TESTCTRL1_REG (0x70) +#define AW88395_TESTCTRL2_REG (0x71) +#define AW88395_EFCTRL1_REG (0x72) +#define AW88395_EFCTRL2_REG (0x73) +#define AW88395_EFWH_REG (0x74) +#define AW88395_EFWM2_REG (0x75) +#define AW88395_EFWM1_REG (0x76) +#define AW88395_EFWL_REG (0x77) +#define AW88395_EFRH_REG (0x78) +#define AW88395_EFRM2_REG (0x79) +#define AW88395_EFRM1_REG (0x7A) +#define AW88395_EFRL_REG (0x7B) +#define AW88395_TM_REG (0x7C) + +enum aw88395_id { + AW88395_CHIP_ID = 0x2049, +}; + +#define AW88395_REG_MAX (0x7D) + +#define AW88395_VOLUME_STEP_DB (6 * 8) + +#define AW88395_UVLS_START_BIT (14) +#define AW88395_UVLS_NORMAL (0) +#define AW88395_UVLS_NORMAL_VALUE \ + (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT) + +#define AW88395_DSPS_START_BIT (12) +#define AW88395_DSPS_BITS_LEN (1) +#define AW88395_DSPS_MASK \ + (~(((1<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT)) + +#define AW88395_DSPS_NORMAL (0) +#define AW88395_DSPS_NORMAL_VALUE \ + (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT) + +#define AW88395_BSTOCS_START_BIT (11) +#define AW88395_BSTOCS_OVER_CURRENT (1) +#define AW88395_BSTOCS_OVER_CURRENT_VALUE \ + (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT) + +#define AW88395_BSTS_START_BIT (9) +#define AW88395_BSTS_FINISHED (1) +#define AW88395_BSTS_FINISHED_VALUE \ + (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT) + +#define AW88395_SWS_START_BIT (8) +#define AW88395_SWS_SWITCHING (1) +#define AW88395_SWS_SWITCHING_VALUE \ + (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT) + +#define AW88395_NOCLKS_START_BIT (5) +#define AW88395_NOCLKS_NO_CLOCK (1) +#define AW88395_NOCLKS_NO_CLOCK_VALUE \ + (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT) + +#define AW88395_CLKS_START_BIT (4) +#define AW88395_CLKS_STABLE (1) +#define AW88395_CLKS_STABLE_VALUE \ + (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT) + +#define AW88395_OCDS_START_BIT (3) +#define AW88395_OCDS_OC (1) +#define AW88395_OCDS_OC_VALUE \ + (AW88395_OCDS_OC << AW88395_OCDS_START_BIT) + +#define AW88395_OTHS_START_BIT (1) +#define AW88395_OTHS_OT (1) +#define AW88395_OTHS_OT_VALUE \ + (AW88395_OTHS_OT << AW88395_OTHS_START_BIT) + +#define AW88395_PLLS_START_BIT (0) +#define AW88395_PLLS_LOCKED (1) +#define AW88395_PLLS_LOCKED_VALUE \ + (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT) + +#define AW88395_BIT_PLL_CHECK \ + (AW88395_CLKS_STABLE_VALUE | \ + AW88395_PLLS_LOCKED_VALUE) + +#define AW88395_BIT_SYSST_CHECK_MASK \ + (~(AW88395_UVLS_NORMAL_VALUE | \ + AW88395_BSTOCS_OVER_CURRENT_VALUE | \ + AW88395_BSTS_FINISHED_VALUE | \ + AW88395_SWS_SWITCHING_VALUE | \ + AW88395_NOCLKS_NO_CLOCK_VALUE | \ + AW88395_CLKS_STABLE_VALUE | \ + AW88395_OCDS_OC_VALUE | \ + AW88395_OTHS_OT_VALUE | \ + AW88395_PLLS_LOCKED_VALUE)) + +#define AW88395_BIT_SYSST_CHECK \ + (AW88395_BSTS_FINISHED_VALUE | \ + AW88395_SWS_SWITCHING_VALUE | \ + AW88395_CLKS_STABLE_VALUE | \ + AW88395_PLLS_LOCKED_VALUE) + +#define AW88395_WDI_START_BIT (6) +#define AW88395_WDI_INT_VALUE (1) +#define AW88395_WDI_INTERRUPT \ + (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT) + +#define AW88395_NOCLKI_START_BIT (5) +#define AW88395_NOCLKI_INT_VALUE (1) +#define AW88395_NOCLKI_INTERRUPT \ + (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT) + +#define AW88395_CLKI_START_BIT (4) +#define AW88395_CLKI_INT_VALUE (1) +#define AW88395_CLKI_INTERRUPT \ + (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT) + +#define AW88395_PLLI_START_BIT (0) +#define AW88395_PLLI_INT_VALUE (1) +#define AW88395_PLLI_INTERRUPT \ + (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT) + +#define AW88395_BIT_SYSINT_CHECK \ + (AW88395_WDI_INTERRUPT | \ + AW88395_CLKI_INTERRUPT | \ + AW88395_NOCLKI_INTERRUPT | \ + AW88395_PLLI_INTERRUPT) + +#define AW88395_HMUTE_START_BIT (8) +#define AW88395_HMUTE_BITS_LEN (1) +#define AW88395_HMUTE_MASK \ + (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT)) + +#define AW88395_HMUTE_DISABLE (0) +#define AW88395_HMUTE_DISABLE_VALUE \ + (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT) + +#define AW88395_HMUTE_ENABLE (1) +#define AW88395_HMUTE_ENABLE_VALUE \ + (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT) + +#define AW88395_RCV_MODE_START_BIT (7) +#define AW88395_RCV_MODE_BITS_LEN (1) +#define AW88395_RCV_MODE_MASK \ + (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT)) + +#define AW88395_RCV_MODE_RECEIVER (1) +#define AW88395_RCV_MODE_RECEIVER_VALUE \ + (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT) + +#define AW88395_DSPBY_START_BIT (2) +#define AW88395_DSPBY_BITS_LEN (1) +#define AW88395_DSPBY_MASK \ + (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT)) + +#define AW88395_DSPBY_WORKING (0) +#define AW88395_DSPBY_WORKING_VALUE \ + (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT) + +#define AW88395_DSPBY_BYPASS (1) +#define AW88395_DSPBY_BYPASS_VALUE \ + (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT) + +#define AW88395_AMPPD_START_BIT (1) +#define AW88395_AMPPD_BITS_LEN (1) +#define AW88395_AMPPD_MASK \ + (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT)) + +#define AW88395_AMPPD_WORKING (0) +#define AW88395_AMPPD_WORKING_VALUE \ + (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT) + +#define AW88395_AMPPD_POWER_DOWN (1) +#define AW88395_AMPPD_POWER_DOWN_VALUE \ + (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT) + +#define AW88395_PWDN_START_BIT (0) +#define AW88395_PWDN_BITS_LEN (1) +#define AW88395_PWDN_MASK \ + (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT)) + +#define AW88395_PWDN_WORKING (0) +#define AW88395_PWDN_WORKING_VALUE \ + (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT) + +#define AW88395_PWDN_POWER_DOWN (1) +#define AW88395_PWDN_POWER_DOWN_VALUE \ + (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT) + +#define AW88395_MUTE_VOL (90 * 8) +#define AW88395_VOLUME_STEP_DB (6 * 8) + +#define AW88395_VOL_6DB_START (6) +#define AW88395_VOL_START_BIT (6) +#define AW88395_VOL_BITS_LEN (10) +#define AW88395_VOL_MASK \ + (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT)) + +#define AW88395_VOL_DEFAULT_VALUE (0) + +#define AW88395_I2STXEN_START_BIT (0) +#define AW88395_I2STXEN_BITS_LEN (1) +#define AW88395_I2STXEN_MASK \ + (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT)) + +#define AW88395_I2STXEN_DISABLE (0) +#define AW88395_I2STXEN_DISABLE_VALUE \ + (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT) + +#define AW88395_I2STXEN_ENABLE (1) +#define AW88395_I2STXEN_ENABLE_VALUE \ + (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT) + +#define AW88395_AGC_DSP_CTL_START_BIT (15) +#define AW88395_AGC_DSP_CTL_BITS_LEN (1) +#define AW88395_AGC_DSP_CTL_MASK \ + (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT)) + +#define AW88395_AGC_DSP_CTL_DISABLE (0) +#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \ + (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT) + +#define AW88395_AGC_DSP_CTL_ENABLE (1) +#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \ + (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT) + +#define AW88395_VDSEL_START_BIT (0) +#define AW88395_VDSEL_BITS_LEN (1) +#define AW88395_VDSEL_MASK \ + (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT)) + +#define AW88395_MEM_CLKSEL_START_BIT (3) +#define AW88395_MEM_CLKSEL_BITS_LEN (1) +#define AW88395_MEM_CLKSEL_MASK \ + (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT)) + +#define AW88395_MEM_CLKSEL_OSC_CLK (0) +#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \ + (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT) + +#define AW88395_MEM_CLKSEL_DAP_HCLK (1) +#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \ + (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT) + +#define AW88395_CCO_MUX_START_BIT (14) +#define AW88395_CCO_MUX_BITS_LEN (1) +#define AW88395_CCO_MUX_MASK \ + (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT)) + +#define AW88395_CCO_MUX_DIVIDED (0) +#define AW88395_CCO_MUX_DIVIDED_VALUE \ + (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT) + +#define AW88395_CCO_MUX_BYPASS (1) +#define AW88395_CCO_MUX_BYPASS_VALUE \ + (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT) + +#define AW88395_EF_VSN_GESLP_START_BIT (0) +#define AW88395_EF_VSN_GESLP_BITS_LEN (10) +#define AW88395_EF_VSN_GESLP_MASK \ + (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT)) + +#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9)) +#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00) + +#define AW88395_EF_ISN_GESLP_START_BIT (0) +#define AW88395_EF_ISN_GESLP_BITS_LEN (10) +#define AW88395_EF_ISN_GESLP_MASK \ + (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT)) + +#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9)) +#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00) + +#define AW88395_CABL_BASE_VALUE (1000) +#define AW88395_ICABLK_FACTOR (1) +#define AW88395_VCABLK_FACTOR (1) +#define AW88395_VCAL_FACTOR (1 << 12) +#define AW88395_VSCAL_FACTOR (16500) +#define AW88395_ISCAL_FACTOR (3667) +#define AW88395_EF_VSENSE_GAIN_SHIFT (0) + +#define AW88395_VCABLK_FACTOR_DAC (2) +#define AW88395_VSCAL_FACTOR_DAC (11790) +#define AW88395_EF_DAC_GESLP_SHIFT (10) +#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5) +#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0) + +#define AW88395_VCALB_ADJ_FACTOR (12) + +#define AW88395_WDT_CNT_START_BIT (0) +#define AW88395_WDT_CNT_BITS_LEN (8) +#define AW88395_WDT_CNT_MASK \ + (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT)) + +#define AW88395_DSP_CFG_ADDR (0x9C80) +#define AW88395_DSP_FW_ADDR (0x8C00) +#define AW88395_DSP_REG_VMAX (0x9C94) +#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00) +#define AW88395_DSP_REG_VCALB (0x9CF7) +#define AW88395_DSP_RE_SHIFT (12) + +#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02) +#define AW88395_DSP_REG_CRC_ADDR (0x9F42) +#define AW88395_DSP_CALI_F0_DELAY (0x9CFD) + +#endif diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 4086b6a53de8..3afcef2dfa35 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -78,11 +78,6 @@ static int bt_sco_probe(struct platform_device *pdev) bt_sco_dai, ARRAY_SIZE(bt_sco_dai)); } -static int bt_sco_remove(struct platform_device *pdev) -{ - return 0; -} - static const struct platform_device_id bt_sco_driver_ids[] = { { .name = "dfbmcs320", @@ -109,7 +104,6 @@ static struct platform_driver bt_sco_driver = { .of_match_table = of_match_ptr(bt_sco_codec_of_match), }, .probe = bt_sco_probe, - .remove = bt_sco_remove, .id_table = bt_sco_driver_ids, }; diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 14403b76c724..32b6a417d0e8 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -134,18 +134,12 @@ static int cq93vc_platform_probe(struct platform_device *pdev) &soc_component_dev_cq93vc, &cq93vc_dai, 1); } -static int cq93vc_platform_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver cq93vc_codec_driver = { .driver = { .name = "cq93vc-codec", }, .probe = cq93vc_platform_probe, - .remove = cq93vc_platform_remove, }; module_platform_driver(cq93vc_codec_driver); diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c index 39d28641429e..1117df4b2f11 100644 --- a/sound/soc/codecs/cs35l45-i2c.c +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -71,4 +71,3 @@ MODULE_DESCRIPTION("I2C CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); -MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c index baaf6e0f4fb9..ffaca07fb267 100644 --- a/sound/soc/codecs/cs35l45-spi.c +++ b/sound/soc/codecs/cs35l45-spi.c @@ -71,4 +71,3 @@ MODULE_DESCRIPTION("SPI CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); -MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 5a2c2e684ef9..4b1320a2e6e9 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -38,7 +38,7 @@ int cs35l45_apply_patch(struct cs35l45_private *cs35l45) return regmap_register_patch(cs35l45->regmap, cs35l45_patch, ARRAY_SIZE(cs35l45_patch)); } -EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES); +EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45); static const struct reg_default cs35l45_defaults[] = { { CS35L45_BLOCK_ENABLES, 0x00003323 }, @@ -126,7 +126,7 @@ const struct regmap_config cs35l45_i2c_regmap = { .readable_reg = cs35l45_readable_reg, .cache_type = REGCACHE_RBTREE, }; -EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45_TABLES); +EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45); const struct regmap_config cs35l45_spi_regmap = { .reg_bits = 32, @@ -142,7 +142,7 @@ const struct regmap_config cs35l45_spi_regmap = { .readable_reg = cs35l45_readable_reg, .cache_type = REGCACHE_RBTREE, }; -EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45_TABLES); +EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45); static const struct { u8 cfg_id; @@ -195,8 +195,4 @@ unsigned int cs35l45_get_clk_freq_id(unsigned int freq) return -EINVAL; } -EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45_TABLES); - -MODULE_DESCRIPTION("ASoC CS35L45 driver tables"); -MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index d15b3b77c7eb..855d9f13e6ff 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -687,4 +687,3 @@ MODULE_DESCRIPTION("ASoC CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c new file mode 100644 index 000000000000..7b539ee55499 --- /dev/null +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only +// cs42l42-sdw.c -- CS42L42 ALSA SoC audio driver SoundWire driver +// +// Copyright (C) 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of_irq.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/sdw.h> +#include <sound/soc.h> + +#include "cs42l42.h" + +#define CS42L42_SDW_CAPTURE_PORT 1 +#define CS42L42_SDW_PLAYBACK_PORT 2 + +/* Register addresses are offset when sent over SoundWire */ +#define CS42L42_SDW_ADDR_OFFSET 0x8000 + +#define CS42L42_SDW_MEM_ACCESS_STATUS 0xd0 +#define CS42L42_SDW_MEM_READ_DATA 0xd8 + +#define CS42L42_SDW_LAST_LATE BIT(3) +#define CS42L42_SDW_CMD_IN_PROGRESS BIT(2) +#define CS42L42_SDW_RDATA_RDY BIT(0) + +#define CS42L42_DELAYED_READ_POLL_US 1 +#define CS42L42_DELAYED_READ_TIMEOUT_US 100 + +static const struct snd_soc_dapm_route cs42l42_sdw_audio_map[] = { + /* Playback Path */ + { "HP", NULL, "MIXER" }, + { "MIXER", NULL, "DACSRC" }, + { "DACSRC", NULL, "Playback" }, + + /* Capture Path */ + { "ADCSRC", NULL, "HS" }, + { "Capture", NULL, "ADCSRC" }, +}; + +static int cs42l42_sdw_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + + if (!cs42l42->init_done) + return -ENODEV; + + return 0; +} + +static int cs42l42_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + int ret; + + if (!sdw_stream) + return -EINVAL; + + /* Needed for PLL configuration when we are notified of new bus config */ + cs42l42->sample_rate = params_rate(params); + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = CS42L42_SDW_PLAYBACK_PORT; + else + port_config.num = CS42L42_SDW_CAPTURE_PORT; + + ret = sdw_stream_add_slave(cs42l42->sdw_peripheral, &stream_config, &port_config, 1, + sdw_stream); + if (ret) { + dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + cs42l42_src_config(dai->component, params_rate(params)); + + return 0; +} + +static int cs42l42_sdw_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + + dev_dbg(dai->dev, "dai_prepare: sclk=%u rate=%u\n", cs42l42->sclk, cs42l42->sample_rate); + + if (!cs42l42->sclk || !cs42l42->sample_rate) + return -EINVAL; + + /* + * At this point we know the sample rate from hw_params, and the SWIRE_CLK from bus_config() + * callback. This could only fail if the ACPI or machine driver are misconfigured to allow + * an unsupported SWIRE_CLK and sample_rate combination. + */ + + return cs42l42_pll_config(dai->component, cs42l42->sclk, cs42l42->sample_rate); +} + +static int cs42l42_sdw_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + sdw_stream_remove_slave(cs42l42->sdw_peripheral, sdw_stream); + cs42l42->sample_rate = 0; + + return 0; +} + +static int cs42l42_sdw_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&slave->dev); + unsigned int pdn_mask; + + if (prepare_ch->num == CS42L42_SDW_PLAYBACK_PORT) + pdn_mask = CS42L42_HP_PDN_MASK; + else + pdn_mask = CS42L42_ADC_PDN_MASK; + + if (state == SDW_OPS_PORT_PRE_PREP) { + dev_dbg(cs42l42->dev, "Prep Port pdn_mask:%x\n", pdn_mask); + regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask); + usleep_range(CS42L42_HP_ADC_EN_TIME_US, CS42L42_HP_ADC_EN_TIME_US + 1000); + } else if (state == SDW_OPS_PORT_POST_DEPREP) { + dev_dbg(cs42l42->dev, "Deprep Port pdn_mask:%x\n", pdn_mask); + regmap_set_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask); + } + + return 0; +} + +static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + if (!sdw_stream) + return 0; + + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void cs42l42_sdw_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static const struct snd_soc_dai_ops cs42l42_sdw_dai_ops = { + .startup = cs42l42_sdw_dai_startup, + .shutdown = cs42l42_sdw_dai_shutdown, + .hw_params = cs42l42_sdw_dai_hw_params, + .prepare = cs42l42_sdw_dai_prepare, + .hw_free = cs42l42_sdw_dai_hw_free, + .mute_stream = cs42l42_mute_stream, + .set_stream = cs42l42_sdw_dai_set_sdw_stream, +}; + +static struct snd_soc_dai_driver cs42l42_sdw_dai = { + .name = "cs42l42-sdw", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* Restrict which rates and formats are supported */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + /* Restrict which rates and formats are supported */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .symmetric_rate = 1, + .ops = &cs42l42_sdw_dai_ops, +}; + +static int cs42l42_sdw_poll_status(struct sdw_slave *peripheral, u8 mask, u8 match) +{ + int ret, sdwret; + + ret = read_poll_timeout(sdw_read_no_pm, sdwret, + (sdwret < 0) || ((sdwret & mask) == match), + CS42L42_DELAYED_READ_POLL_US, CS42L42_DELAYED_READ_TIMEOUT_US, + false, peripheral, CS42L42_SDW_MEM_ACCESS_STATUS); + if (ret == 0) + ret = sdwret; + + if (ret < 0) + dev_err(&peripheral->dev, "MEM_ACCESS_STATUS & %#x for %#x fail: %d\n", + mask, match, ret); + + return ret; +} + +static int cs42l42_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sdw_slave *peripheral = context; + u8 data; + int ret; + + reg += CS42L42_SDW_ADDR_OFFSET; + + ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0); + if (ret < 0) + return ret; + + ret = sdw_read_no_pm(peripheral, reg); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to issue read @0x%x: %d\n", reg, ret); + return ret; + } + + data = (u8)ret; /* possible non-delayed read value */ + ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_ACCESS_STATUS); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to read MEM_ACCESS_STATUS: %d\n", ret); + return ret; + } + + /* If read was not delayed we already have the result */ + if ((ret & CS42L42_SDW_LAST_LATE) == 0) { + *val = data; + return 0; + } + + /* Poll for delayed read completion */ + if ((ret & CS42L42_SDW_RDATA_RDY) == 0) { + ret = cs42l42_sdw_poll_status(peripheral, + CS42L42_SDW_RDATA_RDY, CS42L42_SDW_RDATA_RDY); + if (ret < 0) + return ret; + } + + ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_READ_DATA); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to read READ_DATA: %d\n", ret); + return ret; + } + + *val = (u8)ret; + + return 0; +} + +static int cs42l42_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct sdw_slave *peripheral = context; + int ret; + + ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0); + if (ret < 0) + return ret; + + return sdw_write_no_pm(peripheral, reg + CS42L42_SDW_ADDR_OFFSET, (u8)val); +} + +/* Initialise cs42l42 using SoundWire - this is only called once, during initialisation */ +static void cs42l42_sdw_init(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + int ret; + + regcache_cache_only(cs42l42->regmap, false); + + ret = cs42l42_init(cs42l42); + if (ret < 0) { + regcache_cache_only(cs42l42->regmap, true); + goto err; + } + + /* Write out any cached changes that happened between probe and attach */ + ret = regcache_sync(cs42l42->regmap); + if (ret < 0) + dev_warn(cs42l42->dev, "Failed to sync cache: %d\n", ret); + + /* Disable internal logic that makes clock-stop conditional */ + regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL3, CS42L42_SW_CLK_STP_STAT_SEL_MASK); + +err: + /* This cancels the pm_runtime_get_noresume() call from cs42l42_sdw_probe(). */ + pm_runtime_put_autosuspend(cs42l42->dev); +} + +static int cs42l42_sdw_read_prop(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + struct sdw_slave_prop *prop = &peripheral->prop; + struct sdw_dpn_prop *ports; + + ports = devm_kcalloc(cs42l42->dev, 2, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + prop->source_ports = BIT(CS42L42_SDW_CAPTURE_PORT); + prop->sink_ports = BIT(CS42L42_SDW_PLAYBACK_PORT); + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* DP1 - capture */ + ports[0].num = CS42L42_SDW_CAPTURE_PORT, + ports[0].type = SDW_DPN_FULL, + ports[0].ch_prep_timeout = 10, + prop->src_dpn_prop = &ports[0]; + + /* DP2 - playback */ + ports[1].num = CS42L42_SDW_PLAYBACK_PORT, + ports[1].type = SDW_DPN_FULL, + ports[1].ch_prep_timeout = 10, + prop->sink_dpn_prop = &ports[1]; + + return 0; +} + +static int cs42l42_sdw_update_status(struct sdw_slave *peripheral, + enum sdw_slave_status status) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + + switch (status) { + case SDW_SLAVE_ATTACHED: + dev_dbg(cs42l42->dev, "ATTACHED\n"); + /* + * Initialise codec, this only needs to be done once. + * When resuming from suspend, resume callback will handle re-init of codec, + * using regcache_sync(). + */ + if (!cs42l42->init_done) + cs42l42_sdw_init(peripheral); + break; + case SDW_SLAVE_UNATTACHED: + dev_dbg(cs42l42->dev, "UNATTACHED\n"); + break; + default: + break; + } + + return 0; +} + +static int cs42l42_sdw_bus_config(struct sdw_slave *peripheral, + struct sdw_bus_params *params) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + unsigned int new_sclk = params->curr_dr_freq / 2; + + /* The cs42l42 cannot support a glitchless SWIRE_CLK change. */ + if ((new_sclk != cs42l42->sclk) && cs42l42->stream_use) { + dev_warn(cs42l42->dev, "Rejected SCLK change while audio active\n"); + return -EBUSY; + } + + cs42l42->sclk = new_sclk; + + dev_dbg(cs42l42->dev, "bus_config: sclk=%u c=%u r=%u\n", + cs42l42->sclk, params->col, params->row); + + return 0; +} + +static const struct sdw_slave_ops cs42l42_sdw_ops = { +/* No interrupt callback because only hardware INT is supported for Jack Detect in the CS42L42 */ + .read_prop = cs42l42_sdw_read_prop, + .update_status = cs42l42_sdw_update_status, + .bus_config = cs42l42_sdw_bus_config, + .port_prep = cs42l42_sdw_port_prep, +}; + +static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + + dev_dbg(dev, "Runtime suspend\n"); + + if (!cs42l42->init_done) + return 0; + + /* The host controller could suspend, which would mean no register access */ + regcache_cache_only(cs42l42->regmap, true); + + return 0; +} + +static const struct reg_sequence __maybe_unused cs42l42_soft_reboot_seq[] = { + REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e), +}; + +static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42) +{ + struct sdw_slave *peripheral = cs42l42->sdw_peripheral; + + if (!peripheral->unattach_request) + return 0; + + /* Cannot access registers until master re-attaches. */ + dev_dbg(&peripheral->dev, "Wait for initialization_complete\n"); + if (!wait_for_completion_timeout(&peripheral->initialization_complete, + msecs_to_jiffies(5000))) { + dev_err(&peripheral->dev, "initialization_complete timed out\n"); + return -ETIMEDOUT; + } + + peripheral->unattach_request = 0; + + /* + * After a bus reset there must be a reconfiguration reset to + * reinitialize the internal state of CS42L42. + */ + regmap_multi_reg_write_bypassed(cs42l42->regmap, + cs42l42_soft_reboot_seq, + ARRAY_SIZE(cs42l42_soft_reboot_seq)); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); + regcache_mark_dirty(cs42l42->regmap); + + return 0; +} + +static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev) +{ + static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500}; + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + unsigned int dbnce; + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + if (!cs42l42->init_done) + return 0; + + ret = cs42l42_sdw_handle_unattach(cs42l42); + if (ret < 0) { + return ret; + } else if (ret > 0) { + dbnce = max(cs42l42->ts_dbnc_rise, cs42l42->ts_dbnc_fall); + + if (dbnce > 0) + msleep(ts_dbnce_ms[dbnce]); + } + + regcache_cache_only(cs42l42->regmap, false); + + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); + + return 0; +} + +static int __maybe_unused cs42l42_sdw_resume(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "System resume\n"); + + /* Power-up so it can re-enumerate */ + ret = cs42l42_resume(dev); + if (ret) + return ret; + + /* Wait for re-attach */ + ret = cs42l42_sdw_handle_unattach(cs42l42); + if (ret < 0) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) +{ + struct snd_soc_component_driver *component_drv; + struct device *dev = &peripheral->dev; + struct cs42l42_private *cs42l42; + struct regmap_config *regmap_conf; + struct regmap *regmap; + int irq, ret; + + cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + if (has_acpi_companion(dev)) + irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); + else + irq = of_irq_get(dev->of_node, 0); + + if (irq == -ENOENT) + irq = 0; + else if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); + + regmap_conf = devm_kmemdup(dev, &cs42l42_regmap, sizeof(cs42l42_regmap), GFP_KERNEL); + if (!regmap_conf) + return -ENOMEM; + regmap_conf->reg_bits = 16; + regmap_conf->num_ranges = 0; + regmap_conf->reg_read = cs42l42_sdw_read; + regmap_conf->reg_write = cs42l42_sdw_write; + + regmap = devm_regmap_init(dev, NULL, peripheral, regmap_conf); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(regmap, true); + + component_drv = devm_kmemdup(dev, + &cs42l42_soc_component, + sizeof(cs42l42_soc_component), + GFP_KERNEL); + if (!component_drv) + return -ENOMEM; + + component_drv->dapm_routes = cs42l42_sdw_audio_map; + component_drv->num_dapm_routes = ARRAY_SIZE(cs42l42_sdw_audio_map); + + cs42l42->dev = dev; + cs42l42->regmap = regmap; + cs42l42->sdw_peripheral = peripheral; + cs42l42->irq = irq; + cs42l42->devid = CS42L42_CHIP_ID; + + /* + * pm_runtime is needed to control bus manager suspend, and to + * recover from an unattach_request when the manager suspends. + */ + pm_runtime_set_autosuspend_delay(cs42l42->dev, 3000); + pm_runtime_use_autosuspend(cs42l42->dev); + pm_runtime_mark_last_busy(cs42l42->dev); + pm_runtime_set_active(cs42l42->dev); + pm_runtime_get_noresume(cs42l42->dev); + pm_runtime_enable(cs42l42->dev); + + ret = cs42l42_common_probe(cs42l42, component_drv, &cs42l42_sdw_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int cs42l42_sdw_remove(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + + cs42l42_common_remove(cs42l42); + pm_runtime_disable(cs42l42->dev); + + return 0; +} + +static const struct dev_pm_ops cs42l42_sdw_pm = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume) + SET_RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL) +}; + +static const struct sdw_device_id cs42l42_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x4242, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id); + +static struct sdw_driver cs42l42_sdw_driver = { + .driver = { + .name = "cs42l42-sdw", + .pm = &cs42l42_sdw_pm, + }, + .probe = cs42l42_sdw_probe, + .remove = cs42l42_sdw_remove, + .ops = &cs42l42_sdw_ops, + .id_table = cs42l42_sdw_id, +}; + +module_sdw_driver(cs42l42_sdw_driver); + +MODULE_DESCRIPTION("ASoC CS42L42 SoundWire driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2fefbcf7bd13..e3edaa1a2761 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -20,6 +20,7 @@ #include <linux/slab.h> #include <linux/acpi.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/gpio/consumer.h> @@ -293,6 +294,7 @@ bool cs42l42_readable_register(struct device *dev, unsigned int reg) case CS42L42_SPDIF_SW_CTL1: case CS42L42_SRC_SDIN_FS: case CS42L42_SRC_SDOUT_FS: + case CS42L42_SOFT_RESET_REBOOT: case CS42L42_SPDIF_CTL1: case CS42L42_SPDIF_CTL2: case CS42L42_SPDIF_CTL3: @@ -358,6 +360,7 @@ bool cs42l42_volatile_register(struct device *dev, unsigned int reg) case CS42L42_LOAD_DET_DONE: case CS42L42_DET_STATUS1: case CS42L42_DET_STATUS2: + case CS42L42_SOFT_RESET_REBOOT: return true; default: return false; @@ -523,6 +526,10 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { /* Playback/Capture Requirements */ SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0), + + /* Soundwire SRC power control */ + SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0), }; static const struct snd_soc_dapm_route cs42l42_audio_map[] = { @@ -590,7 +597,6 @@ const struct snd_soc_component_driver cs42l42_soc_component = { .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map), .controls = cs42l42_snd_controls, .num_controls = ARRAY_SIZE(cs42l42_snd_controls), - .idle_bias_on = 1, .endianness = 1, }; EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE); @@ -651,11 +657,11 @@ static const struct cs42l42_pll_params pll_ratio_table[] = { { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} }; -static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk) +int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk, + unsigned int sample_rate) { struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); int i; - u32 fsync; /* Don't reconfigure if there is an audio stream running */ if (cs42l42->stream_use) { @@ -666,6 +672,10 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int } for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + /* MCLKint must be a multiple of the sample rate */ + if (pll_ratio_table[i].mclk_int % sample_rate) + continue; + if (pll_ratio_table[i].sclk == clk) { cs42l42->pll_config = i; @@ -677,40 +687,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int (pll_ratio_table[i].mclk_int != 24000000)) << CS42L42_INTERNAL_FS_SHIFT); - - /* Set up the LRCLK */ - fsync = clk / cs42l42->srate; - if (((fsync * cs42l42->srate) != clk) - || ((fsync % 2) != 0)) { - dev_err(component->dev, - "Unsupported sclk %d/sample rate %d\n", - clk, - cs42l42->srate); - return -EINVAL; - } - /* Set the LRCLK period */ - snd_soc_component_update_bits(component, - CS42L42_FSYNC_P_LOWER, - CS42L42_FSYNC_PERIOD_MASK, - CS42L42_FRAC0_VAL(fsync - 1) << - CS42L42_FSYNC_PERIOD_SHIFT); - snd_soc_component_update_bits(component, - CS42L42_FSYNC_P_UPPER, - CS42L42_FSYNC_PERIOD_MASK, - CS42L42_FRAC1_VAL(fsync - 1) << - CS42L42_FSYNC_PERIOD_SHIFT); - /* Set the LRCLK to 50% duty cycle */ - fsync = fsync / 2; - snd_soc_component_update_bits(component, - CS42L42_FSYNC_PW_LOWER, - CS42L42_FSYNC_PULSE_WIDTH_MASK, - CS42L42_FRAC0_VAL(fsync - 1) << - CS42L42_FSYNC_PULSE_WIDTH_SHIFT); - snd_soc_component_update_bits(component, - CS42L42_FSYNC_PW_UPPER, - CS42L42_FSYNC_PULSE_WIDTH_MASK, - CS42L42_FRAC1_VAL(fsync - 1) << - CS42L42_FSYNC_PULSE_WIDTH_SHIFT); if (pll_ratio_table[i].mclk_src_sel == 0) { /* Pass the clock straight through */ snd_soc_component_update_bits(component, @@ -768,8 +744,9 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int return -EINVAL; } +EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, SND_SOC_CS42L42_CORE); -static void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate) +void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate) { struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); unsigned int fs; @@ -801,6 +778,47 @@ static void cs42l42_src_config(struct snd_soc_component *component, unsigned int CS42L42_CLK_OASRC_SEL_MASK, fs << CS42L42_CLK_OASRC_SEL_SHIFT); } +EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, SND_SOC_CS42L42_CORE); + +static int cs42l42_asp_config(struct snd_soc_component *component, + unsigned int sclk, unsigned int sample_rate) +{ + u32 fsync = sclk / sample_rate; + + /* Set up the LRCLK */ + if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) { + dev_err(component->dev, + "Unsupported sclk %d/sample rate %d\n", + sclk, + sample_rate); + return -EINVAL; + } + /* Set the LRCLK period */ + snd_soc_component_update_bits(component, + CS42L42_FSYNC_P_LOWER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_FSYNC_P_UPPER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + /* Set the LRCLK to 50% duty cycle */ + fsync = fsync / 2; + snd_soc_component_update_bits(component, + CS42L42_FSYNC_PW_LOWER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_FSYNC_PW_UPPER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + + return 0; +} static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -891,13 +909,12 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); unsigned int channels = params_channels(params); unsigned int width = (params_width(params) / 8) - 1; + unsigned int sample_rate = params_rate(params); unsigned int slot_width = 0; unsigned int val = 0; unsigned int bclk; int ret; - cs42l42->srate = params_rate(params); - if (cs42l42->bclk_ratio) { /* machine driver has set the BCLK/samp-rate ratio */ bclk = cs42l42->bclk_ratio * params_rate(params); @@ -954,11 +971,15 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, break; } - ret = cs42l42_pll_config(component, bclk); + ret = cs42l42_pll_config(component, bclk, sample_rate); if (ret) return ret; - cs42l42_src_config(component, params_rate(params)); + ret = cs42l42_asp_config(component, bclk, sample_rate); + if (ret) + return ret; + + cs42l42_src_config(component, sample_rate); return 0; } @@ -998,7 +1019,7 @@ static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai, return 0; } -static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); @@ -1091,6 +1112,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; } +EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, SND_SOC_CS42L42_CORE); #define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ @@ -1633,7 +1655,7 @@ static const struct cs42l42_irq_params irq_params_table[] = { CS42L42_TSRS_PLUG_VAL_MASK} }; -static irqreturn_t cs42l42_irq_thread(int irq, void *data) +irqreturn_t cs42l42_irq_thread(int irq, void *data) { struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data; unsigned int stickies[12]; @@ -1642,9 +1664,11 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) unsigned int current_button_status; unsigned int i; + pm_runtime_get_sync(cs42l42->dev); mutex_lock(&cs42l42->irq_lock); if (cs42l42->suspended || !cs42l42->init_done) { mutex_unlock(&cs42l42->irq_lock); + pm_runtime_put_autosuspend(cs42l42->dev); return IRQ_NONE; } @@ -1747,9 +1771,12 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) } mutex_unlock(&cs42l42->irq_lock); + pm_runtime_mark_last_busy(cs42l42->dev); + pm_runtime_put_autosuspend(cs42l42->dev); return IRQ_HANDLED; } +EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, SND_SOC_CS42L42_CORE); static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42) { @@ -2125,6 +2152,9 @@ int cs42l42_suspend(struct device *dev) u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)]; int i, ret; + if (!cs42l42->init_done) + return 0; + /* * Wait for threaded irq handler to be idle and stop it processing * future interrupts. This ensures a safe disable if the interrupt @@ -2185,6 +2215,9 @@ int cs42l42_resume(struct device *dev) struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); int ret; + if (!cs42l42->init_done) + return 0; + /* * If jack was unplugged and re-plugged during suspend it could * have changed type but the tip-sense state hasn't changed. @@ -2369,6 +2402,18 @@ int cs42l42_init(struct cs42l42_private *cs42l42) if (ret != 0) goto err_shutdown; + /* + * SRC power is linked to ASP power so doesn't work in Soundwire mode. + * Override it and use DAPM to control SRC power for Soundwire. + */ + if (cs42l42->sdw_peripheral) { + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, + CS42L42_SRC_PDN_OVERRIDE_MASK | + CS42L42_DAC_SRC_PDNB_MASK | + CS42L42_ADC_SRC_PDNB_MASK, + CS42L42_SRC_PDN_OVERRIDE_MASK); + } + /* Setup headset detection */ cs42l42_setup_hs_type_detect(cs42l42); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index a72136664112..4bd7b85a5747 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -18,6 +18,7 @@ #include <linux/mutex.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/soundwire/sdw.h> #include <sound/jack.h> #include <sound/cs42l42.h> #include <sound/soc-component.h> @@ -30,13 +31,14 @@ struct cs42l42_private { struct gpio_desc *reset_gpio; struct completion pdn_done; struct snd_soc_jack *jack; + struct sdw_slave *sdw_peripheral; struct mutex irq_lock; int devid; int irq; int pll_config; u32 sclk; + u32 sample_rate; u32 bclk_ratio; - u32 srate; u8 plug_state; u8 hs_type; u8 ts_inv; @@ -62,6 +64,11 @@ extern struct snd_soc_dai_driver cs42l42_dai; bool cs42l42_readable_register(struct device *dev, unsigned int reg); bool cs42l42_volatile_register(struct device *dev, unsigned int reg); +int cs42l42_pll_config(struct snd_soc_component *component, + unsigned int clk, unsigned int sample_rate); +void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate); +int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream); +irqreturn_t cs42l42_irq_thread(int irq, void *data); int cs42l42_suspend(struct device *dev); int cs42l42_resume(struct device *dev); void cs42l42_resume_restore(struct device *dev); diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 544ccbcfc884..0068780fe0a7 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1157,13 +1157,31 @@ static int da7213_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + u8 dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_64; u8 dai_ctrl = 0; u8 fs; + /* Set channels */ + switch (params_channels(params)) { + case 1: + if (da7213->fmt != DA7213_DAI_FORMAT_DSP) { + dev_err(component->dev, "Mono supported only in DSP mode\n"); + return -EINVAL; + } + dai_ctrl |= DA7213_DAI_MONO_MODE_EN; + break; + case 2: + dai_ctrl &= ~(DA7213_DAI_MONO_MODE_EN); + break; + default: + return -EINVAL; + } + /* Set DAI format */ switch (params_width(params)) { case 16: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE; + dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_32; /* 32bit for 1ch and 2ch */ break; case 20: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE; @@ -1224,8 +1242,11 @@ static int da7213_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK, - dai_ctrl); + snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE, + DA7213_DAI_BCLKS_PER_WCLK_MASK, dai_clk_mode); + + snd_soc_component_update_bits(component, DA7213_DAI_CTRL, + DA7213_DAI_WORD_LENGTH_MASK | DA7213_DAI_MONO_MODE_MASK, dai_ctrl); snd_soc_component_write(component, DA7213_SR, fs); return 0; @@ -1300,19 +1321,24 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE; + da7213->fmt = DA7213_DAI_FORMAT_I2S_MODE; break; case SND_SOC_DAIFMT_LEFT_J: dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J; + da7213->fmt = DA7213_DAI_FORMAT_LEFT_J; break; case SND_SOC_DAIFMT_RIGHT_J: dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J; + da7213->fmt = DA7213_DAI_FORMAT_RIGHT_J; break; case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */ dai_ctrl |= DA7213_DAI_FORMAT_DSP; dai_offset = 1; + da7213->fmt = DA7213_DAI_FORMAT_DSP; break; case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */ dai_ctrl |= DA7213_DAI_FORMAT_DSP; + da7213->fmt = DA7213_DAI_FORMAT_DSP; break; default: return -EINVAL; diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 97ccf0ddd2be..4ca9cfdea06d 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -195,6 +195,8 @@ #define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2) #define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2) #define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7213_DAI_MONO_MODE_EN (0x1 << 4) +#define DA7213_DAI_MONO_MODE_MASK (0x1 << 4) #define DA7213_DAI_EN_SHIFT 7 /* DA7213_DIG_ROUTING_DAI = 0x21 */ @@ -542,6 +544,7 @@ struct da7213_priv { bool alc_en; bool fixed_clk_auto_pll; struct da7213_platform_data *pdata; + int fmt; }; #endif /* _DA7213_H */ diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index 4b8ec6f77337..d57b043d6bfe 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -126,12 +126,15 @@ static void hda_codec_unregister_dais(struct hda_codec *codec, struct hda_pcm *pcm; for_each_component_dais_safe(component, dai, save) { + int stream; + list_for_each_entry(pcm, &codec->pcm_list_head, list) { if (strcmp(dai->driver->name, pcm->name)) continue; - snd_soc_dapm_free_widget(dai->playback_widget); - snd_soc_dapm_free_widget(dai->capture_widget); + for_each_pcm_streams(stream) + snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); + snd_soc_unregister_dai(dai); break; } diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 74cbbe16f9ae..01e8ffda2a4b 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -518,7 +518,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); struct hdmi_codec_params hp = { .iec = { .status = { 0 }, @@ -562,7 +562,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int channels = runtime->channels; unsigned int width = snd_pcm_format_width(runtime->format); @@ -597,7 +597,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream, static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); /* Reset daifmt */ memset(cf, 0, sizeof(*cf)); @@ -834,7 +834,8 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai) if (!daifmt) return -ENOMEM; - dai->playback_dma_data = daifmt; + snd_soc_dai_dma_data_set_playback(dai, daifmt); + return 0; } @@ -891,7 +892,7 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) if (ret) return ret; - cf = dai->playback_dma_data; + cf = snd_soc_dai_dma_data_get_playback(dai); cf->fmt = HDMI_SPDIF; return 0; diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c new file mode 100644 index 000000000000..2cc7b9166e69 --- /dev/null +++ b/sound/soc/codecs/idt821034.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// IDT821034 ALSA SoC driver +// +// Copyright 2022 CS GROUP France +// +// Author: Herve Codina <herve.codina@bootlin.com> + +#include <linux/bitrev.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define IDT821034_NB_CHANNEL 4 + +struct idt821034_amp { + u16 gain; + bool is_muted; +}; + +struct idt821034 { + struct spi_device *spi; + struct mutex mutex; + u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + struct { + u8 codec_conf; + struct { + u8 power; + u8 tx_slot; + u8 rx_slot; + u8 slic_conf; + u8 slic_control; + } ch[IDT821034_NB_CHANNEL]; + } cache; + struct { + struct { + struct idt821034_amp amp_out; + struct idt821034_amp amp_in; + } ch[IDT821034_NB_CHANNEL]; + } amps; + int max_ch_playback; + int max_ch_capture; + struct gpio_chip gpio_chip; +}; + +static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + + idt821034->spi_tx_buf = val; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val); + + return spi_sync_transfer(idt821034->spi, xfer, 2); +} + +static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2) +{ + int ret; + + ret = idt821034_8bit_write(idt821034, val1); + if (ret) + return ret; + return idt821034_8bit_write(idt821034, val2); +} + +static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .rx_buf = &idt821034->spi_rx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + int ret; + + idt821034->spi_tx_buf = valw; + + ret = spi_sync_transfer(idt821034->spi, xfer, 2); + if (ret) + return ret; + + *valr = idt821034->spi_rx_buf; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n", + valw, *valr); + + return 0; +} + +/* Available mode for the programming sequence */ +#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2)) +#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2)) +#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2)) + +/* Power values that can be used in 'power' (can be ORed) */ +#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */ +#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */ + +static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + idt821034->cache.ch[ch].rx_slot); + if (ret) + return ret; + } + if (power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + idt821034->cache.ch[ch].tx_slot); + if (ret) + return ret; + } + if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) { + ret = idt821034_2x8bit_write(idt821034, conf, 0); + if (ret) + return ret; + } + + idt821034->cache.ch[ch].power = power; + + return 0; +} + +static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].power; +} + +/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */ +#define IDT821034_CONF_ALAW_MODE BIT(5) +#define IDT821034_CONF_DELAY_MODE BIT(4) + +static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf) +{ + u8 conf; + u8 ts; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf); + + /* codec conf fields are common to all channel. + * Arbitrary use of channel 0 for this configuration. + */ + + /* Set Configuration Register */ + conf = IDT821034_MODE_CODEC(0) | codec_conf; + + /* Update conf value and timeslot register value according + * to cache values + */ + if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) { + conf |= IDT821034_CONF_PWRUP_RX; + ts = idt821034->cache.ch[0].rx_slot; + } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) { + conf |= IDT821034_CONF_PWRUP_TX; + ts = idt821034->cache.ch[0].tx_slot; + } else { + ts = 0x00; + } + + /* Write configuration register and time-slot register */ + ret = idt821034_2x8bit_write(idt821034, conf, ts); + if (ret) + return ret; + + idt821034->cache.codec_conf = codec_conf; + return 0; +} + +static u8 idt821034_get_codec_conf(struct idt821034 *idt821034) +{ + return idt821034->cache.codec_conf; +} + +/* Channel direction values that can be used in 'ch_dir' (can be ORed) */ +#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */ +#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */ + +static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (ch_dir & IDT821034_CH_RX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].rx_slot = ts_num; + } + if (ch_dir & IDT821034_CH_TX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].tx_slot = ts_num; + } + + return 0; +} + +/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */ +#define IDT821034_SLIC_IO1_IN BIT(1) +#define IDT821034_SLIC_IO0_IN BIT(0) + +static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir); + + conf = IDT821034_MODE_SLIC(ch) | slic_dir; + ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_conf = slic_dir; + + return 0; +} + +static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_conf; +} + +static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw); + + /* + * On write, slic_raw is mapped as follow : + * b4: O_4 + * b3: O_3 + * b2: O_2 + * b1: I/O_1 + * b0: I/O_0 + */ + + conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_2x8bit_write(idt821034, conf, slic_raw); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_control = slic_raw; + return 0; +} + +static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_control; +} + +static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw) +{ + u8 val; + int ret; + + /* + * On read, slic_raw is mapped as follow : + * b7: I/O_0 + * b6: I/O_1 + * b5: O_2 + * b4: O_3 + * b3: O_4 + * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value) + * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value) + * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value) + */ + + val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_8bit_write(idt821034, val); + if (ret) + return ret; + + ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw); + if (ret) + return ret; + + dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw); + + return 0; +} + +/* Gain type values that can be used in 'gain_type' (cannot be ORed) */ +#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */ +#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */ + +static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch, + u8 gain_type, u16 gain_val) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n", + ch, gain_type, gain_val, gain_val); + + /* + * The gain programming coefficients should be calculated as: + * Transmit : Coeff_X = round [ gain_X0dB × gain_X ] + * Receive: Coeff_R = round [ gain_R0dB × gain_R ] + * where: + * gain_X0dB = 1820; + * gain_X is the target gain; + * Coeff_X should be in the range of 0 to 8192. + * gain_R0dB = 2506; + * gain_R is the target gain; + * Coeff_R should be in the range of 0 to 8192. + * + * A gain programming coefficient is 14-bit wide and in binary format. + * The 7 Most Significant Bits of the coefficient is called + * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for + * receive path; The 7 Least Significant Bits of the coefficient is + * called GA_LSB_ Transmit for transmit path, or is called + * GA_LSB_Receive for receive path. + * + * An example is given below to clarify the calculation of the + * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB + * gain in receive path: + * + * Linear Code of +3dB = 10^(3/20)= 1.412537545 + * Coeff_X = round (1820 × 1.412537545) = 2571 + * = 0b001010_00001011 + * GA_MSB_Transmit = 0b0010100 + * GA_LSB_Transmit = 0b0001011 + * + * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917 + * Coeff_R= round (2506 × 0.668343917) = 1675 + * = 0b0001101_0001011 + * GA_MSB_Receive = 0b0001101 + * GA_LSB_Receive = 0b0001011 + */ + + conf = IDT821034_MODE_GAIN(ch) | gain_type; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F); + if (ret) + return ret; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F); + if (ret) + return ret; + + return 0; +} + +/* Id helpers used in controls and dapm */ +#define IDT821034_DIR_OUT (1 << 3) +#define IDT821034_DIR_IN (0 << 3) +#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir)) +#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT) +#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN) + +#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03) +#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3)) +#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT) + +static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int val; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + if (IDT821034_ID_IS_OUT(mc->reg)) + val = idt821034->amps.ch[ch].amp_out.gain; + else + val = idt821034->amps.ch[ch].amp_in.gain; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = val & mask; + if (invert) + ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; + else + ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min; + + return 0; +} + +static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + struct idt821034_amp *amp; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret; + u8 gain_type; + u8 ch; + + val = ucontrol->value.integer.value[0]; + if (val > max - min) + return -EINVAL; + + if (invert) + val = (max - val) & mask; + else + val = (val + min) & mask; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(mc->reg)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->gain == val) { + ret = 0; + goto end; + } + + if (!amp->is_muted) { + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); + if (ret) + goto end; + } + + amp->gain = val; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + bool is_muted; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + + mutex_lock(&idt821034->mutex); + is_muted = IDT821034_ID_IS_OUT(id) ? + idt821034->amps.ch[ch].amp_out.is_muted : + idt821034->amps.ch[ch].amp_in.is_muted; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = !is_muted; + + return 0; +} + +static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + struct idt821034_amp *amp; + bool is_mute; + u8 gain_type; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + is_mute = !ucontrol->value.integer.value[0]; + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(id)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->is_muted == is_mute) { + ret = 0; + goto end; + } + + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, + is_mute ? 0 : amp->gain); + if (ret) + goto end; + + amp->is_muted = is_mute; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306); +#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */ +#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */ +#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */ + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029); +#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/ +#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */ +#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */ + +static const struct snd_kcontrol_new idt821034_controls[] = { + /* DAC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + + /* DAC mute control */ + SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + + /* ADC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + + /* ADC mute control */ + SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), +}; + +static int idt821034_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + unsigned int id = w->shift; + u8 power, mask; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; + + mutex_lock(&idt821034->mutex); + + power = idt821034_get_channel_power(idt821034, ch); + if (SND_SOC_DAPM_EVENT_ON(event)) + power |= mask; + else + power &= ~mask; + ret = idt821034_set_channel_power(idt821034, ch, power); + + mutex_unlock(&idt821034->mutex); + + return ret; +} + +static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), +}; + +static const struct snd_soc_dapm_route idt821034_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC0", NULL, "IN0" }, + { "ADC1", NULL, "IN1" }, + { "ADC2", NULL, "IN2" }, + { "ADC3", NULL, "IN3" }, +}; + +static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int width) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int mask; + u8 slot; + int ret; + u8 ch; + + switch (width) { + case 0: /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + mask = tx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_playback = ch; + + mask = rx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_capture = ch; + + return 0; +} + +static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + conf |= IDT821034_CONF_DELAY_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + conf &= ~IDT821034_CONF_DELAY_MODE; + break; + default: + dev_err(dai->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_A_LAW: + conf |= IDT821034_CONF_ALAW_MODE; + break; + case SNDRV_PCM_FORMAT_MU_LAW: + conf &= ~IDT821034_CONF_ALAW_MODE; + break; + default: + dev_err(dai->dev, "Unsupported PCM format 0x%x\n", + params_format(params)); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const unsigned int idt821034_sample_bits[] = {8}; + +static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = { + .list = idt821034_sample_bits, + .count = ARRAY_SIZE(idt821034_sample_bits), +}; + +static int idt821034_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int max_ch = 0; + int ret; + + max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + idt821034->max_ch_playback : idt821034->max_ch_capture; + + /* + * Disable stream support (min = 0, max = 0) if no timeslots were + * configured otherwise, limit the number of channels to those + * configured. + */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + max_ch ? 1 : 0, max_ch); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &idt821034_sample_bits_constr); + if (ret) + return ret; + + return 0; +} + +static u64 idt821034_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops idt821034_dai_ops = { + .startup = idt821034_dai_startup, + .hw_params = idt821034_dai_hw_params, + .set_tdm_slot = idt821034_dai_set_tdm_slot, + .set_fmt = idt821034_dai_set_fmt, + .auto_selectable_formats = idt821034_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats), +}; + +static struct snd_soc_dai_driver idt821034_dai_driver = { + .name = "idt821034", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .ops = &idt821034_dai_ops, +}; + +static int idt821034_reset_audio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + ret = idt821034_set_codec_conf(idt821034, 0); + if (ret) + goto end; + + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; + idt821034->amps.ch[i].amp_out.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, + idt821034->amps.ch[i].amp_out.gain); + if (ret) + goto end; + + idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; + idt821034->amps.ch[i].amp_in.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, + idt821034->amps.ch[i].amp_in.gain); + if (ret) + goto end; + + ret = idt821034_set_channel_power(idt821034, i, 0); + if (ret) + goto end; + } + + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_component_probe(struct snd_soc_component *component) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int ret; + + /* reset idt821034 audio part*/ + ret = idt821034_reset_audio(idt821034); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver idt821034_component_driver = { + .probe = idt821034_component_probe, + .controls = idt821034_controls, + .num_controls = ARRAY_SIZE(idt821034_controls), + .dapm_widgets = idt821034_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets), + .dapm_routes = idt821034_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes), + .endianness = 1, +}; + +#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4) +#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5) + +static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + + slic_raw = idt821034_get_written_slic_raw(idt821034, ch); + if (val) + slic_raw |= mask; + else + slic_raw &= ~mask; + ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); + if (ret) { + dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); +} + +static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + return ret; + } + + /* + * SLIC IOs are read in reverse order compared to write. + * Reverse the read value here in order to have IO0 at lsb (ie same + * order as write) + */ + return !!(bitrev8(slic_raw) & mask); +} + +static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_dir; + + mutex_lock(&idt821034->mutex); + slic_dir = idt821034_get_slic_conf(idt821034, ch); + mutex_unlock(&idt821034->mutex); + + return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + /* Only IO0 and IO1 can be set as input */ + if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) + return -EPERM; + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + idt821034_chip_gpio_set(c, offset, val); + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_reset_gpio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + /* IO0 and IO1 as input for all channels and output IO set to 0 */ + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + ret = idt821034_set_slic_conf(idt821034, i, + IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); + if (ret) + goto end; + + ret = idt821034_write_slic_raw(idt821034, i, 0); + if (ret) + goto end; + + } + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_gpio_init(struct idt821034 *idt821034) +{ + int ret; + + ret = idt821034_reset_gpio(idt821034); + if (ret) + return ret; + + idt821034->gpio_chip.owner = THIS_MODULE; + idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev); + idt821034->gpio_chip.parent = &idt821034->spi->dev; + idt821034->gpio_chip.base = -1; + idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */ + idt821034->gpio_chip.get_direction = idt821034_chip_get_direction; + idt821034->gpio_chip.direction_input = idt821034_chip_direction_input; + idt821034->gpio_chip.direction_output = idt821034_chip_direction_output; + idt821034->gpio_chip.get = idt821034_chip_gpio_get; + idt821034->gpio_chip.set = idt821034_chip_gpio_set; + idt821034->gpio_chip.can_sleep = true; + + return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip, + idt821034); +} + +static int idt821034_spi_probe(struct spi_device *spi) +{ + struct idt821034 *idt821034; + int ret; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL); + if (!idt821034) + return -ENOMEM; + + idt821034->spi = spi; + + mutex_init(&idt821034->mutex); + + spi_set_drvdata(spi, idt821034); + + ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver, + &idt821034_dai_driver, 1); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_GPIOLIB)) + return idt821034_gpio_init(idt821034); + + return 0; +} + +static const struct of_device_id idt821034_of_match[] = { + { .compatible = "renesas,idt821034", }, + { } +}; +MODULE_DEVICE_TABLE(of, idt821034_of_match); + +static const struct spi_device_id idt821034_id_table[] = { + { "idt821034", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, idt821034_id_table); + +static struct spi_driver idt821034_spi_driver = { + .driver = { + .name = "idt821034", + .of_match_table = idt821034_of_match, + }, + .id_table = idt821034_id_table, + .probe = idt821034_spi_probe, +}; + +module_spi_driver(idt821034_spi_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("IDT821034 ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c index d96a4f6c9183..9df58e23d360 100644 --- a/sound/soc/codecs/jz4760.c +++ b/sound/soc/codecs/jz4760.c @@ -287,6 +287,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100); static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100); static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); /* Unconditional controls. */ static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = { @@ -299,6 +300,14 @@ static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = { JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3, REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4760_CODEC_REG_MIX1, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4760_CODEC_REG_MIX2, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + SOC_SINGLE("High-Pass Filter Capture Switch", JZ4760_CODEC_REG_CR4, REG_CR4_ADC_HPF_OFFSET, 1, 0), diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index a9ef9d5ffcc5..a73a7d7a1c0a 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -366,7 +366,7 @@ #define CDC_RX_DSD1_CFG2 (0x0F8C) #define RX_MAX_OFFSET (0x0F8C) -#define MCLK_FREQ 9600000 +#define MCLK_FREQ 19200000 #define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ @@ -2296,10 +2296,8 @@ static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, aif_rst = rx->rx_port_value[widget->shift]; if (!rx_port_value) { - if (aif_rst == 0) { - dev_err(component->dev, "%s:AIF reset already\n", __func__); + if (aif_rst == 0) return 0; - } if (aif_rst > RX_MACRO_AIF4_PB) { dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); return 0; @@ -3441,16 +3439,10 @@ static int swclk_gate_enable(struct clk_hw *hw) } rx_macro_mclk_enable(rx, true); - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, - CDC_RX_SWR_RESET_MASK, - CDC_RX_SWR_RESET); regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, CDC_RX_SWR_CLK_EN_MASK, 1); - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, - CDC_RX_SWR_RESET_MASK, 0); - return 0; } @@ -3579,7 +3571,7 @@ static int rx_macro_probe(struct platform_device *pdev) /* set MCLK and NPL rates */ clk_set_rate(rx->mclk, MCLK_FREQ); - clk_set_rate(rx->npl, 2 * MCLK_FREQ); + clk_set_rate(rx->npl, MCLK_FREQ); ret = clk_prepare_enable(rx->macro); if (ret) @@ -3601,9 +3593,16 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) goto err_fsgen; - ret = rx_macro_register_mclk_output(rx); - if (ret) - goto err_clkout; + /* reset swr block */ + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_RESET_MASK, + CDC_RX_SWR_RESET); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 1); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_RESET_MASK, 0); ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv, rx_macro_dai, @@ -3618,6 +3617,10 @@ static int rx_macro_probe(struct platform_device *pdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); + ret = rx_macro_register_mclk_output(rx); + if (ret) + goto err_clkout; + return 0; err_clkout: diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 2ef62d6edc30..bf27bdd5be20 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -203,7 +203,7 @@ #define TX_MACRO_AMIC_UNMUTE_DELAY_MS 100 #define TX_MACRO_DMIC_HPF_DELAY_MS 300 #define TX_MACRO_AMIC_HPF_DELAY_MS 300 -#define MCLK_FREQ 9600000 +#define MCLK_FREQ 19200000 enum { TX_MACRO_AIF_INVALID = 0, @@ -1861,15 +1861,10 @@ static int swclk_gate_enable(struct clk_hw *hw) } tx_macro_mclk_enable(tx, true); - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, - CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, - CDC_TX_SWR_RESET_MASK, 0x0); - return 0; } @@ -2014,7 +2009,7 @@ static int tx_macro_probe(struct platform_device *pdev) /* set MCLK and NPL rates */ clk_set_rate(tx->mclk, MCLK_FREQ); - clk_set_rate(tx->npl, 2 * MCLK_FREQ); + clk_set_rate(tx->npl, MCLK_FREQ); ret = clk_prepare_enable(tx->macro); if (ret) @@ -2036,9 +2031,15 @@ static int tx_macro_probe(struct platform_device *pdev) if (ret) goto err_fsgen; - ret = tx_macro_register_mclk_output(tx); - if (ret) - goto err_clkout; + /* reset soundwire block */ + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); + + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, + CDC_TX_SWR_CLK_ENABLE); + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_RESET_MASK, 0x0); ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv, tx_macro_dai, @@ -2052,6 +2053,10 @@ static int tx_macro_probe(struct platform_device *pdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); + ret = tx_macro_register_mclk_output(tx); + if (ret) + goto err_clkout; + return 0; err_clkout: diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index b0b6cf29cba3..fd62817d29a0 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1333,17 +1333,9 @@ static int fsgen_gate_enable(struct clk_hw *hw) int ret; ret = va_macro_mclk_enable(va, true); - if (!va->has_swr_master) - return ret; - - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE); - - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_CLK_EN_MASK, - CDC_VA_SWR_CLK_ENABLE); - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_RESET_MASK, 0x0); + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); return ret; } @@ -1524,16 +1516,6 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_mclk; - ret = va_macro_register_fsgen_output(va); - if (ret) - goto err_clkout; - - va->fsgen = clk_hw_get_clk(&va->hw, "fsgen"); - if (IS_ERR(va->fsgen)) { - ret = PTR_ERR(va->fsgen); - goto err_clkout; - } - if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, @@ -1548,6 +1530,15 @@ static int va_macro_probe(struct platform_device *pdev) } + if (va->has_swr_master) { + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE); + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_RESET_MASK, 0x0); + } + ret = devm_snd_soc_register_component(dev, &va_macro_component_drv, va_macro_dais, ARRAY_SIZE(va_macro_dais)); @@ -1560,6 +1551,16 @@ static int va_macro_probe(struct platform_device *pdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); + ret = va_macro_register_fsgen_output(va); + if (ret) + goto err_clkout; + + va->fsgen = clk_hw_get_clk(&va->hw, "fsgen"); + if (IS_ERR(va->fsgen)) { + ret = PTR_ERR(va->fsgen); + goto err_clkout; + } + return 0; err_clkout: diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 5cfe96f6e430..ba7480f3831e 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -1856,10 +1856,8 @@ static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, aif_rst = wsa->rx_port_value[widget->shift]; if (!rx_port_value) { - if (aif_rst == 0) { - dev_err(component->dev, "%s: AIF reset already\n", __func__); + if (aif_rst == 0) return 0; - } if (aif_rst >= WSA_MACRO_RX_MAX) { dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); return 0; @@ -2270,17 +2268,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) } wsa_macro_mclk_enable(wsa, true); - /* reset swr ip */ - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, - CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE); - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE); - /* Bring out of reset */ - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, - CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE); } else { regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, CDC_WSA_SWR_CLK_EN_MASK, 0); @@ -2451,10 +2442,16 @@ static int wsa_macro_probe(struct platform_device *pdev) if (ret) goto err_fsgen; - ret = wsa_macro_register_mclk_output(wsa); - if (ret) - goto err_clkout; + /* reset swr ip */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE); + + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE); + /* Bring out of reset */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE); ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv, wsa_macro_dai, @@ -2468,6 +2465,10 @@ static int wsa_macro_probe(struct platform_device *pdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); + ret = wsa_macro_register_mclk_output(wsa); + if (ret) + goto err_clkout; + return 0; err_clkout: diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 06ed2a938108..b419c49e1e08 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2356,8 +2356,7 @@ static const struct snd_soc_dai_ops max98090_dai_ops = { .no_capture_mute = 1, }; -static struct snd_soc_dai_driver max98090_dai[] = { -{ +static struct snd_soc_dai_driver max98090_dai = { .name = "HiFi", .playback = { .stream_name = "HiFi Playback", @@ -2374,7 +2373,6 @@ static struct snd_soc_dai_driver max98090_dai[] = { .formats = MAX98090_FORMATS, }, .ops = &max98090_dai_ops, -} }; static int max98090_probe(struct snd_soc_component *component) @@ -2594,8 +2592,8 @@ static int max98090_i2c_probe(struct i2c_client *i2c) } ret = devm_snd_soc_register_component(&i2c->dev, - &soc_component_dev_max98090, max98090_dai, - ARRAY_SIZE(max98090_dai)); + &soc_component_dev_max98090, + &max98090_dai, 1); err_enable: return ret; } diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 3cd1be743d9e..c9a2d4dabd3c 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -689,10 +689,7 @@ static int max98373_set_sdw_stream(struct snd_soc_dai *dai, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 71490f11d96a..086ac97e8386 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -776,16 +776,10 @@ static int __init mc13783_codec_probe(struct platform_device *pdev) return ret; } -static int mc13783_codec_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver mc13783_codec_driver = { .driver = { .name = "mc13783-codec", }, - .remove = mc13783_codec_remove, }; module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe); diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index 1aef281a9972..d5006d8de639 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1056,6 +1056,7 @@ static const int update_reg[] = { static int nau8822_probe(struct snd_soc_component *component) { int i; + struct device_node *of_node = component->dev->of_node; /* * Set the update bit in all registers, that have one. This way all @@ -1066,6 +1067,14 @@ static int nau8822_probe(struct snd_soc_component *component) snd_soc_component_update_bits(component, update_reg[i], 0x100, 0x100); + /* Check property to configure the two loudspeaker outputs as + * a single Bridge Tied Load output + */ + if (of_property_read_bool(of_node, "nuvoton,spk-btl")) + snd_soc_component_update_bits(component, + NAU8822_REG_RIGHT_SPEAKER_CONTROL, + NAU8822_RSUBBYP, NAU8822_RSUBBYP); + return 0; } diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index 547ec057f853..646f6bb64bc5 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -187,6 +187,15 @@ /* NAU8822_REG_PLL_K3 (0x27) */ #define NAU8822_PLLK3_MASK 0x1FF +/* NAU8822_REG_RIGHT_SPEAKER_CONTROL (0x2B) */ +#define NAU8822_RMIXMUT 0x20 +#define NAU8822_RSUBBYP 0x10 + +#define NAU8822_RAUXRSUBG_SFT 1 +#define NAU8822_RAUXRSUBG_MASK 0x0E + +#define NAU8822_RAUXSMUT 0x01 + /* System Clock Source */ enum { NAU8822_CLK_MCLK, diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c new file mode 100644 index 000000000000..5dec69be0acb --- /dev/null +++ b/sound/soc/codecs/peb2466.c @@ -0,0 +1,2071 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// peb2466.c -- Infineon PEB2466 ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina <herve.codina@bootlin.com> + +#include <asm/unaligned.h> +#include <linux/clk.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define PEB2466_NB_CHANNEL 4 + +struct peb2466_lookup { + u8 (*table)[4]; + unsigned int count; +}; + +#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \ + sizeof(unsigned int)) + +struct peb2466_lkup_ctrl { + int reg; + unsigned int index; + const struct peb2466_lookup *lookup; + unsigned int tlv_array[PEB2466_TLV_SIZE]; +}; + +struct peb2466 { + struct spi_device *spi; + struct clk *mclk; + struct gpio_desc *reset_gpio; + u8 spi_tx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */ + u8 spi_rx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */ + struct regmap *regmap; + struct { + struct peb2466_lookup ax_lookup; + struct peb2466_lookup ar_lookup; + struct peb2466_lkup_ctrl ax_lkup_ctrl; + struct peb2466_lkup_ctrl ar_lkup_ctrl; + unsigned int tg1_freq_item; + unsigned int tg2_freq_item; + } ch[PEB2466_NB_CHANNEL]; + int max_chan_playback; + int max_chan_capture; + struct { + struct gpio_chip gpio_chip; + struct mutex lock; + struct { + unsigned int xr0; + unsigned int xr1; + unsigned int xr2; + unsigned int xr3; + } cache; + } gpio; +}; + +#define PEB2466_CMD_R (1 << 5) +#define PEB2466_CMD_W (0 << 5) + +#define PEB2466_CMD_MASK 0x18 +#define PEB2466_CMD_XOP 0x18 /* XOP is 0bxxx11xxx */ +#define PEB2466_CMD_SOP 0x10 /* SOP is 0bxxx10xxx */ +#define PEB2466_CMD_COP 0x00 /* COP is 0bxxx0xxxx, handle 0bxxx00xxx */ +#define PEB2466_CMD_COP1 0x08 /* COP is 0bxxx0xxxx, handle 0bxxx01xxx */ + +#define PEB2466_MAKE_XOP(_lsel) (PEB2466_CMD_XOP | (_lsel)) +#define PEB2466_MAKE_SOP(_ad, _lsel) (PEB2466_CMD_SOP | ((_ad) << 6) | (_lsel)) +#define PEB2466_MAKE_COP(_ad, _code) (PEB2466_CMD_COP | ((_ad) << 6) | (_code)) + +#define PEB2466_CR0(_ch) PEB2466_MAKE_SOP(_ch, 0x0) +#define PEB2466_CR0_TH (1 << 7) +#define PEB2466_CR0_IMR1 (1 << 6) +#define PEB2466_CR0_FRX (1 << 5) +#define PEB2466_CR0_FRR (1 << 4) +#define PEB2466_CR0_AX (1 << 3) +#define PEB2466_CR0_AR (1 << 2) +#define PEB2466_CR0_THSEL_MASK (0x3 << 0) +#define PEB2466_CR0_THSEL(_set) ((_set) << 0) + +#define PEB2466_CR1(_ch) PEB2466_MAKE_SOP(_ch, 0x1) +#define PEB2466_CR1_ETG2 (1 << 7) +#define PEB2466_CR1_ETG1 (1 << 6) +#define PEB2466_CR1_PTG2 (1 << 5) +#define PEB2466_CR1_PTG1 (1 << 4) +#define PEB2466_CR1_LAW_MASK (1 << 3) +#define PEB2466_CR1_LAW_ALAW (0 << 3) +#define PEB2466_CR1_LAW_MULAW (1 << 3) +#define PEB2466_CR1_PU (1 << 0) + +#define PEB2466_CR2(_ch) PEB2466_MAKE_SOP(_ch, 0x2) +#define PEB2466_CR3(_ch) PEB2466_MAKE_SOP(_ch, 0x3) +#define PEB2466_CR4(_ch) PEB2466_MAKE_SOP(_ch, 0x4) +#define PEB2466_CR5(_ch) PEB2466_MAKE_SOP(_ch, 0x5) + +#define PEB2466_XR0 PEB2466_MAKE_XOP(0x0) +#define PEB2466_XR1 PEB2466_MAKE_XOP(0x1) +#define PEB2466_XR2 PEB2466_MAKE_XOP(0x2) +#define PEB2466_XR3 PEB2466_MAKE_XOP(0x3) +#define PEB2466_XR4 PEB2466_MAKE_XOP(0x4) +#define PEB2466_XR5 PEB2466_MAKE_XOP(0x5) +#define PEB2466_XR5_MCLK_1536 (0x0 << 6) +#define PEB2466_XR5_MCLK_2048 (0x1 << 6) +#define PEB2466_XR5_MCLK_4096 (0x2 << 6) +#define PEB2466_XR5_MCLK_8192 (0x3 << 6) + +#define PEB2466_XR6 PEB2466_MAKE_XOP(0x6) +#define PEB2466_XR6_PCM_OFFSET(_off) ((_off) << 0) + +#define PEB2466_XR7 PEB2466_MAKE_XOP(0x7) + +#define PEB2466_TH_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x0) +#define PEB2466_TH_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x1) +#define PEB2466_TH_FILTER_P3(_ch) PEB2466_MAKE_COP(_ch, 0x2) +#define PEB2466_IMR1_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x4) +#define PEB2466_IMR1_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x5) +#define PEB2466_FRX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x6) +#define PEB2466_FRR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x7) +#define PEB2466_AX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x8) +#define PEB2466_AR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x9) +#define PEB2466_TG1(_ch) PEB2466_MAKE_COP(_ch, 0xc) +#define PEB2466_TG2(_ch) PEB2466_MAKE_COP(_ch, 0xd) + +static int peb2466_write_byte(struct peb2466 *peb2466, u8 cmd, u8 val) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .len = 2, + }; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W; + peb2466->spi_tx_buf[1] = val; + + dev_dbg(&peb2466->spi->dev, "write byte (cmd %02x) %02x\n", + peb2466->spi_tx_buf[0], peb2466->spi_tx_buf[1]); + + return spi_sync_transfer(peb2466->spi, &xfer, 1); +} + +static int peb2466_read_byte(struct peb2466 *peb2466, u8 cmd, u8 *val) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .rx_buf = &peb2466->spi_rx_buf, + .len = 3, + }; + int ret; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_R; + + ret = spi_sync_transfer(peb2466->spi, &xfer, 1); + if (ret) + return ret; + + if (peb2466->spi_rx_buf[1] != 0x81) { + dev_err(&peb2466->spi->dev, + "spi xfer rd (cmd %02x) invalid ident byte (0x%02x)\n", + peb2466->spi_tx_buf[0], peb2466->spi_rx_buf[1]); + return -EILSEQ; + } + + *val = peb2466->spi_rx_buf[2]; + + dev_dbg(&peb2466->spi->dev, "read byte (cmd %02x) %02x\n", + peb2466->spi_tx_buf[0], *val); + + return 0; +} + +static int peb2466_write_buf(struct peb2466 *peb2466, u8 cmd, const u8 *buf, unsigned int len) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .len = len + 1, + }; + + if (len > 8) + return -EINVAL; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W; + memcpy(&peb2466->spi_tx_buf[1], buf, len); + + dev_dbg(&peb2466->spi->dev, "write buf (cmd %02x, %u) %*ph\n", + peb2466->spi_tx_buf[0], len, len, &peb2466->spi_tx_buf[1]); + + return spi_sync_transfer(peb2466->spi, &xfer, 1); +} + +static int peb2466_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct peb2466 *peb2466 = context; + int ret; + + /* + * Only XOP and SOP commands can be handled as registers. + * COP commands are handled using direct peb2466_write_buf() calls. + */ + switch (reg & PEB2466_CMD_MASK) { + case PEB2466_CMD_XOP: + case PEB2466_CMD_SOP: + ret = peb2466_write_byte(peb2466, reg, val); + break; + default: + dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int peb2466_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct peb2466 *peb2466 = context; + int ret; + u8 tmp; + + /* Only XOP and SOP commands can be handled as registers */ + switch (reg & PEB2466_CMD_MASK) { + case PEB2466_CMD_XOP: + case PEB2466_CMD_SOP: + ret = peb2466_read_byte(peb2466, reg, &tmp); + *val = tmp; + break; + default: + dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static const struct regmap_config peb2466_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, + .reg_write = peb2466_reg_write, + .reg_read = peb2466_reg_read, + .cache_type = REGCACHE_NONE, +}; + +static int peb2466_lkup_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = lkup_ctrl->lookup->count - 1; + return 0; +} + +static int peb2466_lkup_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = lkup_ctrl->index; + return 0; +} + +static int peb2466_lkup_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + unsigned int index; + int ret; + + index = ucontrol->value.integer.value[0]; + if (index >= lkup_ctrl->lookup->count) + return -EINVAL; + + if (index == lkup_ctrl->index) + return 0; + + ret = peb2466_write_buf(peb2466, lkup_ctrl->reg, + lkup_ctrl->lookup->table[index], 4); + if (ret) + return ret; + + lkup_ctrl->index = index; + return 1; /* The value changed */ +} + +static int peb2466_add_lkup_ctrl(struct snd_soc_component *component, + struct peb2466_lkup_ctrl *lkup_ctrl, + const char *name, int min_val, int step) +{ + DECLARE_TLV_DB_SCALE(tlv_array, min_val, step, 0); + struct snd_kcontrol_new control = {0}; + + BUILD_BUG_ON(sizeof(lkup_ctrl->tlv_array) < sizeof(tlv_array)); + memcpy(lkup_ctrl->tlv_array, tlv_array, sizeof(tlv_array)); + + control.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + control.name = name; + control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + control.tlv.p = lkup_ctrl->tlv_array; + control.info = peb2466_lkup_ctrl_info; + control.get = peb2466_lkup_ctrl_get; + control.put = peb2466_lkup_ctrl_put; + control.private_value = (unsigned long)lkup_ctrl; + + return snd_soc_add_component_controls(component, &control, 1); +} + +enum peb2466_tone_freq { + PEB2466_TONE_697HZ, + PEB2466_TONE_800HZ, + PEB2466_TONE_950HZ, + PEB2466_TONE_1000HZ, + PEB2466_TONE_1008HZ, + PEB2466_TONE_2000HZ, +}; + +static const u8 peb2466_tone_lookup[][4] = { + [PEB2466_TONE_697HZ] = {0x0a, 0x33, 0x5a, 0x2c}, + [PEB2466_TONE_800HZ] = {0x12, 0xD6, 0x5a, 0xc0}, + [PEB2466_TONE_950HZ] = {0x1c, 0xf0, 0x5c, 0xc0}, + [PEB2466_TONE_1000HZ] = {0}, /* lookup value not used for 1000Hz */ + [PEB2466_TONE_1008HZ] = {0x1a, 0xae, 0x57, 0x70}, + [PEB2466_TONE_2000HZ] = {0x00, 0x80, 0x50, 0x09}, +}; + +static const char * const peb2466_tone_freq_txt[] = { + [PEB2466_TONE_697HZ] = "697Hz", + [PEB2466_TONE_800HZ] = "800Hz", + [PEB2466_TONE_950HZ] = "950Hz", + [PEB2466_TONE_1000HZ] = "1000Hz", + [PEB2466_TONE_1008HZ] = "1008Hz", + [PEB2466_TONE_2000HZ] = "2000Hz" +}; + +static const struct soc_enum peb2466_tg_freq[][2] = { + [0] = { + SOC_ENUM_SINGLE(PEB2466_TG1(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [1] = { + SOC_ENUM_SINGLE(PEB2466_TG1(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [2] = { + SOC_ENUM_SINGLE(PEB2466_TG1(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [3] = { + SOC_ENUM_SINGLE(PEB2466_TG1(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + } +}; + +static int peb2466_tg_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + switch (e->reg) { + case PEB2466_TG1(0): + ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg1_freq_item; + break; + case PEB2466_TG2(0): + ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg2_freq_item; + break; + case PEB2466_TG1(1): + ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg1_freq_item; + break; + case PEB2466_TG2(1): + ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg2_freq_item; + break; + case PEB2466_TG1(2): + ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg1_freq_item; + break; + case PEB2466_TG2(2): + ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg2_freq_item; + break; + case PEB2466_TG1(3): + ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg1_freq_item; + break; + case PEB2466_TG2(3): + ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg2_freq_item; + break; + default: + return -EINVAL; + } + return 0; +} + +static int peb2466_tg_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *tg_freq_item; + u8 cr1_reg, cr1_mask; + unsigned int index; + int ret; + + index = ucontrol->value.enumerated.item[0]; + + if (index >= ARRAY_SIZE(peb2466_tone_lookup)) + return -EINVAL; + + switch (e->reg) { + case PEB2466_TG1(0): + tg_freq_item = &peb2466->ch[0].tg1_freq_item; + cr1_reg = PEB2466_CR1(0); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(0): + tg_freq_item = &peb2466->ch[0].tg2_freq_item; + cr1_reg = PEB2466_CR1(0); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(1): + tg_freq_item = &peb2466->ch[1].tg1_freq_item; + cr1_reg = PEB2466_CR1(1); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(1): + tg_freq_item = &peb2466->ch[1].tg2_freq_item; + cr1_reg = PEB2466_CR1(1); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(2): + tg_freq_item = &peb2466->ch[2].tg1_freq_item; + cr1_reg = PEB2466_CR1(2); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(2): + tg_freq_item = &peb2466->ch[2].tg2_freq_item; + cr1_reg = PEB2466_CR1(2); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(3): + tg_freq_item = &peb2466->ch[3].tg1_freq_item; + cr1_reg = PEB2466_CR1(3); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(3): + tg_freq_item = &peb2466->ch[3].tg2_freq_item; + cr1_reg = PEB2466_CR1(3); + cr1_mask = PEB2466_CR1_PTG2; + break; + default: + return -EINVAL; + } + + if (index == *tg_freq_item) + return 0; + + if (index == PEB2466_TONE_1000HZ) { + ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, 0); + if (ret) + return ret; + } else { + ret = peb2466_write_buf(peb2466, e->reg, peb2466_tone_lookup[index], 4); + if (ret) + return ret; + ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, cr1_mask); + if (ret) + return ret; + } + + *tg_freq_item = index; + return 1; /* The value changed */ +} + +static const struct snd_kcontrol_new peb2466_ch0_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(0), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(0), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(0), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch1_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(1), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(1), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(1), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch2_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(2), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(2), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(2), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(3), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(3), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_controls[] = { + /* Attenuators */ + SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0), + SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0), + SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0), + SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0), + + /* Amplifiers */ + SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0), + SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0), + SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0), + SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0), + + /* Tone generators */ + SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC1 TG1 Freq", peb2466_tg_freq[1][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC2 TG1 Freq", peb2466_tg_freq[2][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC3 TG1 Freq", peb2466_tg_freq[3][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + + SOC_ENUM_EXT("DAC0 TG2 Freq", peb2466_tg_freq[0][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC1 TG2 Freq", peb2466_tg_freq[1][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC2 TG2 Freq", peb2466_tg_freq[2][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC3 TG2 Freq", peb2466_tg_freq[3][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), +}; + +static const struct snd_soc_dapm_widget peb2466_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("CH0 PWR", PEB2466_CR1(0), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH1 PWR", PEB2466_CR1(1), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH2 PWR", PEB2466_CR1(2), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH3 PWR", PEB2466_CR1(3), 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("CH0 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH1 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH2 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH3 DIN", "Playback", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SIGGEN("CH0 TG1"), + SND_SOC_DAPM_SIGGEN("CH1 TG1"), + SND_SOC_DAPM_SIGGEN("CH2 TG1"), + SND_SOC_DAPM_SIGGEN("CH3 TG1"), + + SND_SOC_DAPM_SIGGEN("CH0 TG2"), + SND_SOC_DAPM_SIGGEN("CH1 TG2"), + SND_SOC_DAPM_SIGGEN("CH2 TG2"), + SND_SOC_DAPM_SIGGEN("CH3 TG2"), + + SND_SOC_DAPM_MIXER("DAC0 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch0_out_mix_controls, + ARRAY_SIZE(peb2466_ch0_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC1 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch1_out_mix_controls, + ARRAY_SIZE(peb2466_ch1_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC2 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch2_out_mix_controls, + ARRAY_SIZE(peb2466_ch2_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC3 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch3_out_mix_controls, + ARRAY_SIZE(peb2466_ch3_out_mix_controls)), + + SND_SOC_DAPM_PGA("DAC0 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC1 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC2 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC3 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), + + SND_SOC_DAPM_DAC("ADC0", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC1", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC2", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC3", "Capture", SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route peb2466_dapm_routes[] = { + { "CH0 DIN", NULL, "CH0 PWR" }, + { "CH1 DIN", NULL, "CH1 PWR" }, + { "CH2 DIN", NULL, "CH2 PWR" }, + { "CH3 DIN", NULL, "CH3 PWR" }, + + { "CH0 TG1", NULL, "CH0 PWR" }, + { "CH1 TG1", NULL, "CH1 PWR" }, + { "CH2 TG1", NULL, "CH2 PWR" }, + { "CH3 TG1", NULL, "CH3 PWR" }, + + { "CH0 TG2", NULL, "CH0 PWR" }, + { "CH1 TG2", NULL, "CH1 PWR" }, + { "CH2 TG2", NULL, "CH2 PWR" }, + { "CH3 TG2", NULL, "CH3 PWR" }, + + { "DAC0 Mixer", "TG1 Switch", "CH0 TG1" }, + { "DAC0 Mixer", "TG2 Switch", "CH0 TG2" }, + { "DAC0 Mixer", "Voice Switch", "CH0 DIN" }, + { "DAC0 Mixer", NULL, "CH0 DIN" }, + + { "DAC1 Mixer", "TG1 Switch", "CH1 TG1" }, + { "DAC1 Mixer", "TG2 Switch", "CH1 TG2" }, + { "DAC1 Mixer", "Voice Switch", "CH1 DIN" }, + { "DAC1 Mixer", NULL, "CH1 DIN" }, + + { "DAC2 Mixer", "TG1 Switch", "CH2 TG1" }, + { "DAC2 Mixer", "TG2 Switch", "CH2 TG2" }, + { "DAC2 Mixer", "Voice Switch", "CH2 DIN" }, + { "DAC2 Mixer", NULL, "CH2 DIN" }, + + { "DAC3 Mixer", "TG1 Switch", "CH3 TG1" }, + { "DAC3 Mixer", "TG2 Switch", "CH3 TG2" }, + { "DAC3 Mixer", "Voice Switch", "CH3 DIN" }, + { "DAC3 Mixer", NULL, "CH3 DIN" }, + + { "DAC0 PGA", NULL, "DAC0 Mixer" }, + { "DAC1 PGA", NULL, "DAC1 Mixer" }, + { "DAC2 PGA", NULL, "DAC2 Mixer" }, + { "DAC3 PGA", NULL, "DAC3 Mixer" }, + + { "OUT0", NULL, "DAC0 PGA" }, + { "OUT1", NULL, "DAC1 PGA" }, + { "OUT2", NULL, "DAC2 PGA" }, + { "OUT3", NULL, "DAC3 PGA" }, + + { "ADC0", NULL, "IN0" }, + { "ADC1", NULL, "IN1" }, + { "ADC2", NULL, "IN2" }, + { "ADC3", NULL, "IN3" }, + + { "ADC0", NULL, "CH0 PWR" }, + { "ADC1", NULL, "CH1 PWR" }, + { "ADC2", NULL, "CH2 PWR" }, + { "ADC3", NULL, "CH3 PWR" }, +}; + +static int peb2466_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int chan; + unsigned int mask; + u8 slot; + int ret; + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + mask = tx_mask; + slot = 0; + chan = 0; + while (mask && chan < PEB2466_NB_CHANNEL) { + if (mask & 0x1) { + ret = regmap_write(peb2466->regmap, PEB2466_CR5(chan), slot); + if (ret) { + dev_err(dai->dev, "chan %d set tx tdm slot failed (%d)\n", + chan, ret); + return ret; + } + chan++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, PEB2466_NB_CHANNEL); + return -EINVAL; + } + peb2466->max_chan_playback = chan; + + mask = rx_mask; + slot = 0; + chan = 0; + while (mask && chan < PEB2466_NB_CHANNEL) { + if (mask & 0x1) { + ret = regmap_write(peb2466->regmap, PEB2466_CR4(chan), slot); + if (ret) { + dev_err(dai->dev, "chan %d set rx tdm slot failed (%d)\n", + chan, ret); + return ret; + } + chan++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, PEB2466_NB_CHANNEL); + return -EINVAL; + } + peb2466->max_chan_capture = chan; + + return 0; +} + +static int peb2466_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + u8 xr6; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + xr6 = PEB2466_XR6_PCM_OFFSET(1); + break; + case SND_SOC_DAIFMT_DSP_B: + xr6 = PEB2466_XR6_PCM_OFFSET(0); + break; + default: + dev_err(dai->dev, "Unsupported format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return regmap_write(peb2466->regmap, PEB2466_XR6, xr6); +} + +static int peb2466_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int ch; + int ret; + u8 cr1; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_MU_LAW: + cr1 = PEB2466_CR1_LAW_MULAW; + break; + case SNDRV_PCM_FORMAT_A_LAW: + cr1 = PEB2466_CR1_LAW_ALAW; + break; + default: + dev_err(&peb2466->spi->dev, "Unsupported format 0x%x\n", + params_format(params)); + return -EINVAL; + } + + for (ch = 0; ch < PEB2466_NB_CHANNEL; ch++) { + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR1(ch), + PEB2466_CR1_LAW_MASK, cr1); + if (ret) + return ret; + } + + return 0; +} + +static const unsigned int peb2466_sample_bits[] = {8}; + +static struct snd_pcm_hw_constraint_list peb2466_sample_bits_constr = { + .list = peb2466_sample_bits, + .count = ARRAY_SIZE(peb2466_sample_bits), +}; + +static int peb2466_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int max_ch; + int ret; + + max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + peb2466->max_chan_playback : peb2466->max_chan_capture; + + /* + * Disable stream support (min = 0, max = 0) if no timeslots were + * configured. + */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + max_ch ? 1 : 0, max_ch); + if (ret < 0) + return ret; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &peb2466_sample_bits_constr); +} + +static u64 peb2466_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops peb2466_dai_ops = { + .startup = peb2466_dai_startup, + .hw_params = peb2466_dai_hw_params, + .set_tdm_slot = peb2466_dai_set_tdm_slot, + .set_fmt = peb2466_dai_set_fmt, + .auto_selectable_formats = peb2466_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats), +}; + +static struct snd_soc_dai_driver peb2466_dai_driver = { + .name = "peb2466", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = PEB2466_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = PEB2466_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .ops = &peb2466_dai_ops, +}; + +static int peb2466_reset_audio(struct peb2466 *peb2466) +{ + static const struct reg_sequence reg_reset[] = { + { .reg = PEB2466_XR6, .def = 0x00 }, + + { .reg = PEB2466_CR5(0), .def = 0x00 }, + { .reg = PEB2466_CR4(0), .def = 0x00 }, + { .reg = PEB2466_CR3(0), .def = 0x00 }, + { .reg = PEB2466_CR2(0), .def = 0x00 }, + { .reg = PEB2466_CR1(0), .def = 0x00 }, + { .reg = PEB2466_CR0(0), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(1), .def = 0x00 }, + { .reg = PEB2466_CR4(1), .def = 0x00 }, + { .reg = PEB2466_CR3(1), .def = 0x00 }, + { .reg = PEB2466_CR2(1), .def = 0x00 }, + { .reg = PEB2466_CR1(1), .def = 0x00 }, + { .reg = PEB2466_CR0(1), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(2), .def = 0x00 }, + { .reg = PEB2466_CR4(2), .def = 0x00 }, + { .reg = PEB2466_CR3(2), .def = 0x00 }, + { .reg = PEB2466_CR2(2), .def = 0x00 }, + { .reg = PEB2466_CR1(2), .def = 0x00 }, + { .reg = PEB2466_CR0(2), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(3), .def = 0x00 }, + { .reg = PEB2466_CR4(3), .def = 0x00 }, + { .reg = PEB2466_CR3(3), .def = 0x00 }, + { .reg = PEB2466_CR2(3), .def = 0x00 }, + { .reg = PEB2466_CR1(3), .def = 0x00 }, + { .reg = PEB2466_CR0(3), .def = PEB2466_CR0_IMR1 }, + }; + static const u8 imr1_p1[8] = {0x00, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x00}; + static const u8 imr1_p2[8] = {0x7F, 0xFF, 0x00, 0x00, 0x90, 0x14, 0x40, 0x08}; + static const u8 zero[8] = {0}; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + peb2466->ch[i].tg1_freq_item = PEB2466_TONE_1000HZ; + peb2466->ch[i].tg2_freq_item = PEB2466_TONE_1000HZ; + + /* + * Even if not used, disabling IM/R1 filter is not recommended. + * Instead, we must configure it with default coefficients and + * enable it. + * The filter will be enabled right after (in the following + * regmap_multi_reg_write() call). + */ + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), imr1_p1, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), imr1_p2, 8); + if (ret) + return ret; + + /* Set all other filters coefficients to zero */ + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), zero, 4); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), zero, 4); + if (ret) + return ret; + } + + return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset)); +} + +static int peb2466_fw_parse_thfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw TH filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * TH_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: TH-Filter coefficients part1 + * - @9 8 bytes: TH-Filter coefficients part2 + * - @17 8 bytes: TH-Filter coefficients part3 + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_TH, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), data + 1, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), data + 9, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), data + 17, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_TH | PEB2466_CR0_THSEL_MASK, + PEB2466_CR0_TH | PEB2466_CR0_THSEL(i)); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_imr1filter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw IM/R1 filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * IMR1_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: IM/R1-Filter coefficients part1 + * - @9 8 bytes: IM/R1-Filter coefficients part2 + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_IMR1, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), data + 1, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), data + 9, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_IMR1, PEB2466_CR0_IMR1); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_frxfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw FRX filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * FRX_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: FRX-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), data + 1, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRX, PEB2466_CR0_FRX); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_frrfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw FRR filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * FRR_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: FRR-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), data + 1, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRR, PEB2466_CR0_FRR); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_axfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw AX filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * AX_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 4 bytes: AX-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), data + 1, 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, PEB2466_CR0_AX); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_arfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw AR filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * AR_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 4 bytes: AR-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), data + 1, 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, PEB2466_CR0_AR); + if (ret) + return ret; + } + return 0; +} + +static const char * const peb2466_ax_ctrl_names[] = { + "ADC0 Capture Volume", + "ADC1 Capture Volume", + "ADC2 Capture Volume", + "ADC3 Capture Volume", +}; + +static int peb2466_fw_parse_axtable(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct peb2466_lkup_ctrl *lkup_ctrl; + struct peb2466_lookup *lookup; + u8 (*table)[4]; + u32 table_size; + u32 init_index; + s32 min_val; + s32 step; + u8 mask; + int ret; + int i; + + /* + * AX_TABLE TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 32bits signed: Min table value in centi dB (MinVal) + * ie -300 means -3.0 dB + * - @5 32bits signed: Step from on item to other item in centi dB (Step) + * ie 25 means 0.25 dB) + * - @9 32bits unsigned: Item index in the table to use for the initial + * value + * - @13 N*4 bytes: Table composed of 4 bytes items. + * Each item correspond to an AX filter value. + * + * The conversion from raw value item in the table to/from the value in + * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB. + */ + + /* Check Lng and extract the table size. */ + if (lng < 13 || ((lng - 13) % 4)) { + dev_err(component->dev, "fw AX table lng %u invalid\n", lng); + return -EINVAL; + } + table_size = lng - 13; + + min_val = get_unaligned_be32(data + 1); + step = get_unaligned_be32(data + 5); + init_index = get_unaligned_be32(data + 9); + if (init_index >= (table_size / 4)) { + dev_err(component->dev, "fw AX table index %u out of table[%u]\n", + init_index, table_size / 4); + return -EINVAL; + } + + dev_info(component->dev, + "fw AX table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n", + *data, min_val, step, table_size / 4, init_index, + 4, data + 13 + (init_index * 4)); + + BUILD_BUG_ON(sizeof(*table) != 4); + table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL); + if (!table) + return -ENOMEM; + memcpy(table, data + 13, table_size); + + mask = *data; + BUILD_BUG_ON(ARRAY_SIZE(peb2466_ax_ctrl_names) != ARRAY_SIZE(peb2466->ch)); + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + lookup = &peb2466->ch[i].ax_lookup; + lookup->table = table; + lookup->count = table_size / 4; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), + lookup->table[init_index], 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, PEB2466_CR0_AX); + if (ret) + return ret; + + lkup_ctrl = &peb2466->ch[i].ax_lkup_ctrl; + lkup_ctrl->lookup = lookup; + lkup_ctrl->reg = PEB2466_AX_FILTER(i); + lkup_ctrl->index = init_index; + + ret = peb2466_add_lkup_ctrl(component, lkup_ctrl, + peb2466_ax_ctrl_names[i], + min_val, step); + if (ret) + return ret; + } + return 0; +} + +static const char * const peb2466_ar_ctrl_names[] = { + "DAC0 Playback Volume", + "DAC1 Playback Volume", + "DAC2 Playback Volume", + "DAC3 Playback Volume", +}; + +static int peb2466_fw_parse_artable(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct peb2466_lkup_ctrl *lkup_ctrl; + struct peb2466_lookup *lookup; + u8 (*table)[4]; + u32 table_size; + u32 init_index; + s32 min_val; + s32 step; + u8 mask; + int ret; + int i; + + /* + * AR_TABLE TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 32bits signed: Min table value in centi dB (MinVal) + * ie -300 means -3.0 dB + * - @5 32bits signed: Step from on item to other item in centi dB (Step) + * ie 25 means 0.25 dB) + * - @9 32bits unsigned: Item index in the table to use for the initial + * value + * - @13 N*4 bytes: Table composed of 4 bytes items. + * Each item correspond to an AR filter value. + * + * The conversion from raw value item in the table to/from the value in + * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB. + */ + + /* Check Lng and extract the table size. */ + if (lng < 13 || ((lng - 13) % 4)) { + dev_err(component->dev, "fw AR table lng %u invalid\n", lng); + return -EINVAL; + } + table_size = lng - 13; + + min_val = get_unaligned_be32(data + 1); + step = get_unaligned_be32(data + 5); + init_index = get_unaligned_be32(data + 9); + if (init_index >= (table_size / 4)) { + dev_err(component->dev, "fw AR table index %u out of table[%u]\n", + init_index, table_size / 4); + return -EINVAL; + } + + dev_info(component->dev, + "fw AR table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n", + *data, min_val, step, table_size / 4, init_index, + 4, data + 13 + (init_index * 4)); + + BUILD_BUG_ON(sizeof(*table) != 4); + table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL); + if (!table) + return -ENOMEM; + memcpy(table, data + 13, table_size); + + mask = *data; + BUILD_BUG_ON(ARRAY_SIZE(peb2466_ar_ctrl_names) != ARRAY_SIZE(peb2466->ch)); + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + lookup = &peb2466->ch[i].ar_lookup; + lookup->table = table; + lookup->count = table_size / 4; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), + lookup->table[init_index], 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, PEB2466_CR0_AR); + if (ret) + return ret; + + lkup_ctrl = &peb2466->ch[i].ar_lkup_ctrl; + lkup_ctrl->lookup = lookup; + lkup_ctrl->reg = PEB2466_AR_FILTER(i); + lkup_ctrl->index = init_index; + + ret = peb2466_add_lkup_ctrl(component, lkup_ctrl, + peb2466_ar_ctrl_names[i], + min_val, step); + if (ret) + return ret; + } + return 0; +} + +struct peb2466_fw_tag_def { + u16 tag; + u32 lng_min; + u32 lng_max; + int (*parse)(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data); +}; + +#define PEB2466_TAG_DEF_LNG_EQ(__tag, __lng, __parse) { \ + .tag = __tag, \ + .lng_min = __lng, \ + .lng_max = __lng, \ + .parse = __parse, \ +} + +#define PEB2466_TAG_DEF_LNG_MIN(__tag, __lng_min, __parse) { \ + .tag = __tag, \ + .lng_min = __lng_min, \ + .lng_max = U32_MAX, \ + .parse = __parse, \ +} + +static const struct peb2466_fw_tag_def peb2466_fw_tag_defs[] = { + /* TH FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0001, 1 + 3 * 8, peb2466_fw_parse_thfilter), + /* IMR1 FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0002, 1 + 2 * 8, peb2466_fw_parse_imr1filter), + /* FRX FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0003, 1 + 8, peb2466_fw_parse_frxfilter), + /* FRR FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0004, 1 + 8, peb2466_fw_parse_frrfilter), + /* AX FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0005, 1 + 4, peb2466_fw_parse_axfilter), + /* AR FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0006, 1 + 4, peb2466_fw_parse_arfilter), + /* AX TABLE */ + PEB2466_TAG_DEF_LNG_MIN(0x0105, 1 + 3 * 4, peb2466_fw_parse_axtable), + /* AR TABLE */ + PEB2466_TAG_DEF_LNG_MIN(0x0106, 1 + 3 * 4, peb2466_fw_parse_artable), +}; + +static const struct peb2466_fw_tag_def *peb2466_fw_get_tag_def(u16 tag) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(peb2466_fw_tag_defs); i++) { + if (peb2466_fw_tag_defs[i].tag == tag) + return &peb2466_fw_tag_defs[i]; + } + return NULL; +} + +static int peb2466_fw_parse(struct snd_soc_component *component, + const u8 *data, size_t size) +{ + const struct peb2466_fw_tag_def *tag_def; + size_t left; + const u8 *buf; + u16 val16; + u16 tag; + u32 lng; + int ret; + + /* + * Coefficients firmware binary structure (16bits and 32bits are + * big-endian values). + * + * @0, 16bits: Magic (0x2466) + * @2, 16bits: Version (0x0100 for version 1.0) + * @4, 2+4+N bytes: TLV block + * @4+(2+4+N) bytes: Next TLV block + * ... + * + * Detail of a TLV block: + * @0, 16bits: Tag + * @2, 32bits: Lng + * @6, lng bytes: Data + * + * The detail the Data for a given TLV Tag is provided in the related + * parser. + */ + + left = size; + buf = data; + + if (left < 4) { + dev_err(component->dev, "fw size %zu, exp at least 4\n", left); + return -EINVAL; + } + + /* Check magic */ + val16 = get_unaligned_be16(buf); + if (val16 != 0x2466) { + dev_err(component->dev, "fw magic 0x%04x exp 0x2466\n", val16); + return -EINVAL; + } + buf += 2; + left -= 2; + + /* Check version */ + val16 = get_unaligned_be16(buf); + if (val16 != 0x0100) { + dev_err(component->dev, "fw magic 0x%04x exp 0x0100\n", val16); + return -EINVAL; + } + buf += 2; + left -= 2; + + while (left) { + if (left < 6) { + dev_err(component->dev, "fw %td/%zu left %zu, exp at least 6\n", + buf - data, size, left); + return -EINVAL; + } + /* Check tag and lng */ + tag = get_unaligned_be16(buf); + lng = get_unaligned_be32(buf + 2); + tag_def = peb2466_fw_get_tag_def(tag); + if (!tag_def) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x unknown\n", + buf - data, size, tag); + return -EINVAL; + } + if (lng < tag_def->lng_min || lng > tag_def->lng_max) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, exp [%u;%u]\n", + buf - data, size, tag, lng, tag_def->lng_min, tag_def->lng_max); + return -EINVAL; + } + buf += 6; + left -= 6; + if (left < lng) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, left %zu\n", + buf - data, size, tag, lng, left); + return -EINVAL; + } + + /* TLV block is valid -> parse the data part */ + ret = tag_def->parse(component, tag, lng, buf); + if (ret) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u parse failed\n", + buf - data, size, tag, lng); + return ret; + } + + buf += lng; + left -= lng; + } + return 0; +} + +static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_name, component->dev); + if (ret) + return ret; + + ret = peb2466_fw_parse(component, fw->data, fw->size); + release_firmware(fw); + + return ret; +} + +static int peb2466_component_probe(struct snd_soc_component *component) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + const char *firmware_name; + int ret; + + /* reset peb2466 audio part */ + ret = peb2466_reset_audio(peb2466); + if (ret) + return ret; + + ret = of_property_read_string(peb2466->spi->dev.of_node, + "firmware-name", &firmware_name); + if (ret) + return (ret == -EINVAL) ? 0 : ret; + + return peb2466_load_coeffs(component, firmware_name); +} + +static const struct snd_soc_component_driver peb2466_component_driver = { + .probe = peb2466_component_probe, + .controls = peb2466_controls, + .num_controls = ARRAY_SIZE(peb2466_controls), + .dapm_widgets = peb2466_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(peb2466_dapm_widgets), + .dapm_routes = peb2466_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(peb2466_dapm_routes), + .endianness = 1, +}; + +/* + * The mapping used for the relationship between the gpio offset and the + * physical pin is the following: + * + * offset pin + * 0 SI1_0 + * 1 SI1_1 + * 2 SI2_0 + * 3 SI2_1 + * 4 SI3_0 + * 5 SI3_1 + * 6 SI4_0 + * 7 SI4_1 + * 8 SO1_0 + * 9 SO1_1 + * 10 SO2_0 + * 11 SO2_1 + * 12 SO3_0 + * 13 SO3_1 + * 14 SO4_0 + * 15 SO4_1 + * 16 SB1_0 + * 17 SB1_1 + * 18 SB2_0 + * 19 SB2_1 + * 20 SB3_0 + * 21 SB3_1 + * 22 SB4_0 + * 23 SB4_1 + * 24 SB1_2 + * 25 SB2_2 + * 26 SB3_2 + * 27 SB4_2 + */ + +static int peb2466_chip_gpio_offset_to_data_regmask(unsigned int offset, + unsigned int *xr_reg, + unsigned int *mask) +{ + if (offset < 16) { + /* + * SIx_{0,1} and SOx_{0,1} + * Read accesses read SIx_{0,1} values + * Write accesses write SOx_{0,1} values + */ + *xr_reg = PEB2466_XR0; + *mask = (1 << (offset % 8)); + return 0; + } + if (offset < 24) { + /* SBx_{0,1} */ + *xr_reg = PEB2466_XR1; + *mask = (1 << (offset - 16)); + return 0; + } + if (offset < 28) { + /* SBx_2 */ + *xr_reg = PEB2466_XR3; + *mask = (1 << (offset - 24 + 4)); + return 0; + } + return -EINVAL; +} + +static int peb2466_chip_gpio_offset_to_dir_regmask(unsigned int offset, + unsigned int *xr_reg, + unsigned int *mask) +{ + if (offset < 16) { + /* Direction cannot be changed for these GPIOs */ + return -EINVAL; + } + if (offset < 24) { + *xr_reg = PEB2466_XR2; + *mask = (1 << (offset - 16)); + return 0; + } + if (offset < 28) { + *xr_reg = PEB2466_XR3; + *mask = (1 << (offset - 24)); + return 0; + } + return -EINVAL; +} + +static unsigned int *peb2466_chip_gpio_get_cache(struct peb2466 *peb2466, + unsigned int xr_reg) +{ + unsigned int *cache; + + switch (xr_reg) { + case PEB2466_XR0: + cache = &peb2466->gpio.cache.xr0; + break; + case PEB2466_XR1: + cache = &peb2466->gpio.cache.xr1; + break; + case PEB2466_XR2: + cache = &peb2466->gpio.cache.xr2; + break; + case PEB2466_XR3: + cache = &peb2466->gpio.cache.xr3; + break; + default: + cache = NULL; + break; + } + return cache; +} + +static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int xr_reg, + unsigned int mask, unsigned int val) +{ + unsigned int tmp; + unsigned int *cache; + int ret; + + /* + * Read and write accesses use different peb2466 internal signals (input + * signals on reads and output signals on writes). regmap_update_bits + * cannot be used to read/modify/write the value. + * So, a specific cache value is used. + */ + + mutex_lock(&peb2466->gpio.lock); + + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); + if (!cache) { + ret = -EINVAL; + goto end; + } + + tmp = *cache; + tmp &= ~mask; + tmp |= val; + + ret = regmap_write(peb2466->regmap, xr_reg, tmp); + if (ret) + goto end; + + *cache = tmp; + ret = 0; + +end: + mutex_unlock(&peb2466->gpio.lock); + return ret; +} + +static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* + * SIx_{0,1} signals cannot be set and writing the related + * register will change the SOx_{0,1} signals + */ + dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n", + offset); + return; + } + + ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n", + offset, ret); + return; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0); + if (ret) { + dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + } +} + +static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + bool use_cache = false; + unsigned int *cache; + unsigned int xr_reg; + unsigned int mask; + unsigned int val; + int ret; + + if (offset >= 8 && offset < 16) { + /* + * SOx_{0,1} signals cannot be read. Reading the related + * register will read the SIx_{0,1} signals. + * Use the cache to get value; + */ + use_cache = true; + } + + ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot get gpio %d (%d)\n", + offset, ret); + return -EINVAL; + } + + if (use_cache) { + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); + if (!cache) + return -EINVAL; + val = *cache; + } else { + ret = regmap_read(peb2466->regmap, xr_reg, &val); + if (ret) { + dev_err(&peb2466->spi->dev, "get gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + } + + return !!(val & mask); +} + +static int peb2466_chip_get_direction(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + unsigned int val; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return GPIO_LINE_DIRECTION_IN; + } + if (offset < 16) { + /* SOx_{0,1} */ + return GPIO_LINE_DIRECTION_OUT; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot get gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = regmap_read(peb2466->regmap, xr_reg, &val); + if (ret) { + dev_err(&peb2466->spi->dev, "get dir gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int peb2466_chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return 0; + } + if (offset < 16) { + /* SOx_{0,1} */ + return -EINVAL; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, 0); + if (ret) { + dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return 0; +} + +static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return -EINVAL; + } + + peb2466_chip_gpio_set(c, offset, val); + + if (offset < 16) { + /* SOx_{0,1} */ + return 0; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, mask); + if (ret) { + dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return 0; +} + +static int peb2466_reset_gpio(struct peb2466 *peb2466) +{ + static const struct reg_sequence reg_reset[] = { + /* Output pins at 0, input/output pins as input */ + { .reg = PEB2466_XR0, .def = 0 }, + { .reg = PEB2466_XR1, .def = 0 }, + { .reg = PEB2466_XR2, .def = 0 }, + { .reg = PEB2466_XR3, .def = 0 }, + }; + + peb2466->gpio.cache.xr0 = 0; + peb2466->gpio.cache.xr1 = 0; + peb2466->gpio.cache.xr2 = 0; + peb2466->gpio.cache.xr3 = 0; + + return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset)); +} + +static int peb2466_gpio_init(struct peb2466 *peb2466) +{ + int ret; + + mutex_init(&peb2466->gpio.lock); + + ret = peb2466_reset_gpio(peb2466); + if (ret) + return ret; + + peb2466->gpio.gpio_chip.owner = THIS_MODULE; + peb2466->gpio.gpio_chip.label = dev_name(&peb2466->spi->dev); + peb2466->gpio.gpio_chip.parent = &peb2466->spi->dev; + peb2466->gpio.gpio_chip.base = -1; + peb2466->gpio.gpio_chip.ngpio = 28; + peb2466->gpio.gpio_chip.get_direction = peb2466_chip_get_direction; + peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input; + peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output; + peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get; + peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set; + peb2466->gpio.gpio_chip.can_sleep = true; + + return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip, + peb2466); +} + +static int peb2466_spi_probe(struct spi_device *spi) +{ + struct peb2466 *peb2466; + unsigned long mclk_rate; + int ret; + u8 xr5; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + peb2466 = devm_kzalloc(&spi->dev, sizeof(*peb2466), GFP_KERNEL); + if (!peb2466) + return -ENOMEM; + + peb2466->spi = spi; + + peb2466->regmap = devm_regmap_init(&peb2466->spi->dev, NULL, peb2466, + &peb2466_regmap_config); + if (IS_ERR(peb2466->regmap)) + return PTR_ERR(peb2466->regmap); + + peb2466->reset_gpio = devm_gpiod_get_optional(&peb2466->spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(peb2466->reset_gpio)) + return PTR_ERR(peb2466->reset_gpio); + + peb2466->mclk = devm_clk_get(&peb2466->spi->dev, "mclk"); + if (IS_ERR(peb2466->mclk)) + return PTR_ERR(peb2466->mclk); + ret = clk_prepare_enable(peb2466->mclk); + if (ret) + return ret; + + if (peb2466->reset_gpio) { + gpiod_set_value_cansleep(peb2466->reset_gpio, 1); + udelay(4); + gpiod_set_value_cansleep(peb2466->reset_gpio, 0); + udelay(4); + } + + spi_set_drvdata(spi, peb2466); + + mclk_rate = clk_get_rate(peb2466->mclk); + switch (mclk_rate) { + case 1536000: + xr5 = PEB2466_XR5_MCLK_1536; + break; + case 2048000: + xr5 = PEB2466_XR5_MCLK_2048; + break; + case 4096000: + xr5 = PEB2466_XR5_MCLK_4096; + break; + case 8192000: + xr5 = PEB2466_XR5_MCLK_8192; + break; + default: + dev_err(&peb2466->spi->dev, "Unsupported clock rate %lu\n", + mclk_rate); + ret = -EINVAL; + goto failed; + } + ret = regmap_write(peb2466->regmap, PEB2466_XR5, xr5); + if (ret) { + dev_err(&peb2466->spi->dev, "Setting MCLK failed (%d)\n", ret); + goto failed; + } + + ret = devm_snd_soc_register_component(&spi->dev, &peb2466_component_driver, + &peb2466_dai_driver, 1); + if (ret) + goto failed; + + if (IS_ENABLED(CONFIG_GPIOLIB)) { + ret = peb2466_gpio_init(peb2466); + if (ret) + goto failed; + } + + return 0; + +failed: + clk_disable_unprepare(peb2466->mclk); + return ret; +} + +static void peb2466_spi_remove(struct spi_device *spi) +{ + struct peb2466 *peb2466 = spi_get_drvdata(spi); + + clk_disable_unprepare(peb2466->mclk); +} + +static const struct of_device_id peb2466_of_match[] = { + { .compatible = "infineon,peb2466", }, + { } +}; +MODULE_DEVICE_TABLE(of, peb2466_of_match); + +static const struct spi_device_id peb2466_id_table[] = { + { "peb2466", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, peb2466_id_table); + +static struct spi_driver peb2466_spi_driver = { + .driver = { + .name = "peb2466", + .of_match_table = peb2466_of_match, + }, + .id_table = peb2466_id_table, + .probe = peb2466_spi_probe, + .remove = peb2466_spi_remove, +}; + +module_spi_driver(peb2466_spi_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("PEB2466 ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index ca2790d63b71..45544b530d3d 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -508,10 +508,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index e6294cc7a995..91edfd5eee08 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -507,10 +507,7 @@ static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } @@ -584,7 +581,7 @@ static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream, * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now */ -static struct sdw_slave_ops rt1316_slave_ops = { +static const struct sdw_slave_ops rt1316_slave_ops = { .read_prop = rt1316_read_prop, .update_status = rt1316_update_status, }; diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index f85f5ab2c6d0..c34975f958c4 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -575,10 +575,7 @@ static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } @@ -697,7 +694,7 @@ static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream, * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now */ -static struct sdw_slave_ops rt1318_slave_ops = { +static const struct sdw_slave_ops rt1318_slave_ops = { .read_prop = rt1318_read_prop, .update_status = rt1318_update_status, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 708e55b7431a..139257055507 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1838,9 +1838,14 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); unsigned int reg_val = 0; unsigned int pll_bit = 0; + int ret; switch (clk_id) { case RT5640_SCLK_S_MCLK: + ret = clk_set_rate(rt5640->mclk, freq); + if (ret) + return ret; + reg_val |= RT5640_SCLK_SRC_MCLK; break; case RT5640_SCLK_S_PLL1: @@ -2717,6 +2722,10 @@ static int rt5640_probe(struct snd_soc_component *component) snd_soc_component_update_bits(component, RT5640_IN1_IN2, RT5640_IN_DF2, RT5640_IN_DF2); + if (device_property_read_bool(component->dev, "realtek,lout-differential")) + snd_soc_component_update_bits(component, RT5640_DUMMY1, + RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF); + if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", &val) == 0 && val) { dmic1_data_pin = val - 1; diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index f58b88e3325b..9847a1ae01f4 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -1978,6 +1978,8 @@ #define RT5640_ZCD_HP_EN (0x1 << 15) /* General Control 1 (0xfa) */ +#define RT5640_EN_LOUT_DF (0x1 << 14) +#define RT5640_EN_LOUT_DF_SFT 14 #define RT5640_M_MONO_ADC_L (0x1 << 13) #define RT5640_M_MONO_ADC_L_SFT 13 #define RT5640_M_MONO_ADC_R (0x1 << 12) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 620ecbfa4a7a..7c7cbb6362ea 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3157,7 +3157,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse snd_soc_dapm_force_enable_pin(dapm, "LDO2"); snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); - if (!dapm->card->instantiated) { + if (!snd_soc_card_is_instantiated(dapm->card)) { /* Power up necessary bits for JD if dapm is not ready yet */ regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 6e66cc218fa8..17afaef85c77 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -1298,7 +1298,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } - while (!rt5665->component->card->instantiated) { + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { pr_debug("%s\n", __func__); usleep_range(10000, 15000); } @@ -4748,7 +4748,7 @@ static void rt5665_calibrate_handler(struct work_struct *work) struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, calibrate_work.work); - while (!rt5665->component->card->instantiated) { + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { pr_debug("%s\n", __func__); usleep_range(10000, 15000); } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index beb0951ff680..ecf3b0527dbe 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -1022,8 +1022,8 @@ static void rt5668_jack_detect_handler(struct work_struct *work) container_of(work, struct rt5668_priv, jack_detect_work.work); int val, btn_type; - if (!rt5668->component || !rt5668->component->card || - !rt5668->component->card->instantiated) { + if (!rt5668->component || + !snd_soc_card_is_instantiated(rt5668->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5668->jack_detect_work, msecs_to_jiffies(15)); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index d8a573dcb771..5f80a5d59b65 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -107,10 +107,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 7e3eb65afe16..f6c798b65c08 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -1094,8 +1094,8 @@ void rt5682_jack_detect_handler(struct work_struct *work) struct snd_soc_dapm_context *dapm; int val, btn_type; - if (!rt5682->component || !rt5682->component->card || - !rt5682->component->card->instantiated) { + if (!rt5682->component || + !snd_soc_card_is_instantiated(rt5682->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(15)); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index f5e5dbc3b0f0..9c34dca58f54 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -834,8 +834,8 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) struct snd_soc_dapm_context *dapm; int val, btn_type; - if (!rt5682s->component || !rt5682s->component->card || - !rt5682s->component->card->instantiated) { + if (!rt5682s->component || + !snd_soc_card_is_instantiated(rt5682s->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work, msecs_to_jiffies(15)); @@ -2895,6 +2895,9 @@ static int rt5682s_suspend(struct snd_soc_component *component) { struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + if (rt5682s->irq) + disable_irq(rt5682s->irq); + cancel_delayed_work_sync(&rt5682s->jack_detect_work); cancel_delayed_work_sync(&rt5682s->jd_check_work); @@ -2919,6 +2922,9 @@ static int rt5682s_resume(struct snd_soc_component *component) &rt5682s->jack_detect_work, msecs_to_jiffies(0)); } + if (rt5682s->irq) + enable_irq(rt5682s->irq); + return 0; } #else @@ -3259,7 +3265,9 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5682s", rt5682s); - if (ret) + if (!ret) + rt5682s->irq = i2c->irq; + else dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index 67f86a38a1cc..caa7733b430f 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -1472,6 +1472,7 @@ struct rt5682s_priv { int pll_comb; int jack_type; + unsigned int irq; int irq_work_delay_time; int wclk_enabled; }; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 6534c9b51442..659ce26e9f3b 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -163,7 +163,7 @@ static void rt700_jack_detect_handler(struct work_struct *work) if (!rt700->hs_jack) return; - if (!rt700->component->card || !rt700->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt700->component->card)) return; reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; @@ -887,10 +887,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 88a8392a58ed..e23cec4c457d 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -338,7 +338,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt711_sdca_slave_ops = { +static const struct sdw_slave_ops rt711_sdca_slave_ops = { .read_prop = rt711_sdca_read_prop, .interrupt_callback = rt711_sdca_interrupt_callback, .update_status = rt711_sdca_update_status, diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index b78dd5994edb..c65abe812a4c 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -295,7 +295,7 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work) if (!rt711->hs_jack) return; - if (!rt711->component->card || !rt711->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt711->component->card)) return; /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ @@ -463,6 +463,21 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) RT711_HP_JD_FINAL_RESULT_CTL_JD12, RT711_HP_JD_FINAL_RESULT_CTL_JD12); break; + case RT711_JD2_100K: + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL3, 0xa47e); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL, + RT711_JD2_DIGITAL_MODE_SEL); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | + RT711_JD2_2PORT_100K_DECODE_MASK | RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_100K_DECODE_HP | RT711_HP_JD_SEL_JD2); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI, + RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI_OFF); + break; default: dev_warn(rt711->component->dev, "Wrong JD source\n"); break; @@ -1234,10 +1249,7 @@ static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 498ca687c47b..10e3c801b813 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -127,12 +127,17 @@ struct sdw_stream_data { /* jack detect control 2 (0x09)(NID:20h) */ #define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13) +#define RT711_JD2_2PORT_100K_DECODE_MASK (0x1 << 12) +#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12) #define RT711_HP_JD_SEL_JD1 (0x0 << 1) #define RT711_HP_JD_SEL_JD2 (0x1 << 1) /* CC DET1 (0x11)(NID:20h) */ #define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10) #define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10) +#define RT711_POW_CC1_AGPI (0x1 << 5) +#define RT711_POW_CC1_AGPI_ON (0x1 << 5) +#define RT711_POW_CC1_AGPI_OFF (0x0 << 5) /* Parameter & Verb control (0x1a)(NID:20h) */ #define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) @@ -226,7 +231,8 @@ enum { enum rt711_sdca_jd_src { RT711_JD_NULL, RT711_JD1, - RT711_JD2 + RT711_JD2, + RT711_JD2_100K }; enum rt711_sdca_ver { diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 78e1da9b0738..862f50950565 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -243,7 +243,7 @@ static void rt711_jack_detect_handler(struct work_struct *work) if (!rt711->hs_jack) return; - if (!rt711->component->card || !rt711->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt711->component->card)) return; if (pm_runtime_status_suspended(rt711->slave->dev.parent)) { @@ -976,10 +976,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c new file mode 100644 index 000000000000..3f319459dfec --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> +#include "rt712-sdca.h" +#include "rt712-sdca-sdw.h" + +static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x201f: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2230 ... 0x2232: + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case 0x3201: + case 0x320c: + case 0x3301 ... 0x3303: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x202d ... 0x202f: + case 0x2230: + case 0x2f01: + case 0x2f35: + case 0x320c: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x200008e: + case 0x5300000 ... 0x530000e: + case 0x5400000 ... 0x540000e: + case 0x5600000 ... 0x5600008: + case 0x5700000 ... 0x570000d: + case 0x5800000 ... 0x5800021: + case 0x5900000 ... 0x5900028: + case 0x5a00000 ... 0x5a00009: + case 0x5b00000 ... 0x5b00051: + case 0x5c00000 ... 0x5c0009a: + case 0x5d00000 ... 0x5d00009: + case 0x5f00000 ... 0x5f00030: + case 0x6100000 ... 0x6100068: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R): + return true; + default: + return false; + } +} + +static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200001a: + case 0x2000024: + case 0x2000046: + case 0x200008a: + case 0x5800000: + case 0x5800001: + case 0x6100008: + return true; + default: + return false; + } +} + +static const struct regmap_config rt712_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt712_sdca_readable_register, + .volatile_reg = rt712_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt712_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt712_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt712_sdca_mbq_readable_register, + .volatile_reg = rt712_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt712_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt712_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + + /* Update the status */ + rt712->status = status; + + if (status == SDW_SLAVE_UNATTACHED) + rt712->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt712->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt712_sdca_jack_init set. + */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt712_sdca_io_init(&slave->dev, slave); +} + +static int rt712_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(4); /* BITMAP: 00010000 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 1380; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt712_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + dev_dbg(&slave->dev, + "%s control_port_stat=%x, sdca_cascade=%x", __func__, + status->control_port, status->sdca_cascade); + + if (cancel_delayed_work_sync(&rt712->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt712->scp_sdca_stat2) + scp_sdca_stat2 = rt712->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt712->disable_irq_lock); + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt712->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt712->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt712->scp_sdca_stat2 |= scp_sdca_stat2; + + do { + /* clear flag */ + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); + + if (status->sdca_cascade && !rt712->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt712->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt712->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt712_sdca_slave_ops = { + .read_prop = rt712_sdca_read_prop, + .interrupt_callback = rt712_sdca_interrupt_callback, + .update_status = rt712_sdca_update_status, +}; + +static int rt712_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt712_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + + if (rt712->hw_init) { + cancel_delayed_work_sync(&rt712->jack_detect_work); + cancel_delayed_work_sync(&rt712->jack_btn_check_work); + } + + if (rt712->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt712->calibrate_mutex); + mutex_destroy(&rt712->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt712_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt712_sdca_id); + +static int __maybe_unused rt712_sdca_dev_suspend(struct device *dev) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + + if (!rt712->hw_init) + return 0; + + cancel_delayed_work_sync(&rt712->jack_detect_work); + cancel_delayed_work_sync(&rt712->jack_btn_check_work); + + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev) +{ + struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt712_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt712_sdca->disable_irq_lock); + rt712_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt712_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt712_sdca_dev_suspend(dev); +} + +#define RT712_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt712->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT712_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt712->regmap, false); + regcache_sync(rt712->regmap); + regcache_cache_only(rt712->mbq_regmap, false); + regcache_sync(rt712->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt712_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt712_sdca_sdw_driver = { + .driver = { + .name = "rt712-sdca", + .owner = THIS_MODULE, + .pm = &rt712_sdca_pm, + }, + .probe = rt712_sdca_sdw_probe, + .remove = rt712_sdca_sdw_remove, + .ops = &rt712_sdca_slave_ops, + .id_table = rt712_sdca_id, +}; +module_sdw_driver(rt712_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h new file mode 100644 index 000000000000..4be22ccd8561 --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt712-sdca-sdw.h -- RT712 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT712_SDW_H__ +#define __RT712_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt712_sdca_reg_defaults[] = { + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x01 }, + { 0x2f35, 0x01 }, + { 0x2f36, 0xcf }, + { 0x2f50, 0x0f }, + { 0x2f54, 0x01 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x09 }, + { 0x2f5a, 0x01 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x05 }, + { 0x2f5d, 0x05 }, + { 0x3201, 0x01 }, + { 0x320c, 0x00 }, + { 0x3301, 0x01 }, + { 0x3302, 0x00 }, + { 0x3303, 0x1f }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt712_sdca_mbq_defaults[] = { + { 0x2000004, 0xaa01 }, + { 0x200000e, 0x21e0 }, + { 0x2000024, 0x01ba }, + { 0x200004a, 0x8830 }, + { 0x2000067, 0xf100 }, + { 0x5800000, 0x1893 }, + { 0x5b00000, 0x0407 }, + { 0x5b00005, 0x0000 }, + { 0x5b00029, 0x3fff }, + { 0x5b0002a, 0xf000 }, + { 0x5f00008, 0x7000 }, + { 0x610000e, 0x0007 }, + { 0x6100022, 0x2828 }, + { 0x6100023, 0x2929 }, + { 0x6100026, 0x2c29 }, + { 0x610002c, 0x4150 }, + { 0x6100045, 0x0860 }, + { 0x6100046, 0x0029 }, + { 0x6100053, 0x3fff }, + { 0x6100055, 0x0000 }, + { 0x6100060, 0x0000 }, + { 0x6100064, 0x8000 }, + { 0x6100065, 0x0000 }, + { 0x6100067, 0xff12 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, +}; + +#endif /* __RT712_SDW_H__ */ diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c new file mode 100644 index 000000000000..8d2fa769bb2e --- /dev/null +++ b/sound/soc/codecs/rt712-sdca.c @@ -0,0 +1,1340 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pm_runtime.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/slab.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "rt712-sdca.h" + +static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt712_sdca_index_read(rt712, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt712_sdca_index_write(rt712, nid, reg, tmp); +} + +static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) +{ + unsigned int val, loop_rc = 0, loop_dc = 0; + struct device *dev; + struct regmap *regmap = rt712->regmap; + int chk_cnt = 100; + int ret = 0; + + mutex_lock(&rt712->calibrate_mutex); + dev = regmap_get_device(regmap); + + /* Set HP-JD source from JD1 */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + + /* FSM switch to calibration manual mode */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); + + /* Calibration setting */ + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0x7883); + + /* W1C Trigger DC calibration (HP & Class-D) */ + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0xf893); + + /* wait for calibration process */ + rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + for (loop_dc = 0; loop_dc < chk_cnt && + (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { + usleep_range(10000, 11000); + ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + if (ret < 0) + goto _cali_fail_; + } + if (loop_dc == chk_cnt) + dev_err(dev, "%s, calibration time-out!\n", __func__); + + if (loop_dc == chk_cnt || loop_rc == chk_cnt) + ret = -ETIMEDOUT; + +_cali_fail_: + /* Enable Rldet in FSM */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4500); + + /* Sensing Lch+Rch */ + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL1, 0x040f); + + /* Sine gen path control */ + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL5, 0x0000); + + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_DIGITAL_MISC_CTRL4, 0x0010); + + mutex_unlock(&rt712->calibrate_mutex); + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), + &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt712->regmap, + RT712_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + + return btn_type; +} + +static int rt712_sdca_headset_detect(struct rt712_sdca_priv *rt712) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt712->jack_type = 0; + break; + case 0x03: + rt712->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt712->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0), + det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt712->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt712_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt712_sdca_priv *rt712 = + container_of(work, struct rt712_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt712->hs_jack) + return; + + if (!rt712->component->card || !rt712->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ + if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt712_sdca_headset_detect(rt712); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt712_sdca_button_detect(rt712); + + if (rt712->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt712->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt712->jack_type); + dev_dbg(&rt712->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt712->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); + + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt712_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt712_sdca_priv *rt712 = + container_of(work, struct rt712_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt712->regmap, + RT712_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + } else { + rt712->jack_type = 0; + } + + dev_dbg(&rt712->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) +{ + mutex_lock(&rt712->calibrate_mutex); + + if (rt712->hs_jack) { + /* Enable HID1 event & set button RTC mode */ + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL5, 0xfff0); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL0, 0x1100, 0x1100); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL7, 0xf000, 0x0000); + + /* detected_mode_change_event_en & hid1_push_button_event_en */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0c00, 0x0c00); + /* ge_inbox_en */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL2, 0x0020, 0x0000); + + switch (rt712->jd_src) { + case RT712_JD1: + /* Set HP-JD source from JD1 */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + break; + default: + dev_warn(rt712->component->dev, "Wrong JD source\n"); + break; + } + + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__); + + /* trigger GE interrupt */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0080, 0x0080); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0080, 0x0000); + } else { + /* disable HID1 & detected_mode_change event */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0c00, 0x0000); + + dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); + } + + mutex_unlock(&rt712->calibrate_mutex); +} + +static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712->hs_jack = hs_jack; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt712_sdca_jack_init(rt712); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt712_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt712->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt712->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue == gain_l_val && rvalue == gain_r_val) + return 0; + + /* Lch*/ + regmap_write(rt712->mbq_regmap, mc->reg, gain_l_val); + /* Rch */ + regmap_write(rt712->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt712->mbq_regmap, mc->reg, &read_l); + regmap_read(rt712->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return 1; + + return -EIO; +} + +static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt712->mbq_regmap, mc->reg, &read_l); + regmap_read(rt712->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else + ctl_r = ctl_l; + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, + RT712_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, + RT712_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt712_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt712->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt712->fu0f_mixer_r_mute; + return 0; +} + +static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int err; + + if (rt712->fu0f_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt712->fu0f_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt712->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt712->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt712_sdca_set_fu0f_capture_ctl(rt712); + if (err < 0) + return err; + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt712_sdca_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x57, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x3f, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), + 8, 3, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv), +}; + +static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x57, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), +}; + +static int rt712_sdca_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask = 0x3300; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val); + + val = val & mask; + switch (val) { + case 0x3000: + val = 1; + break; + case 0x0300: + val = 0; + break; + } + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt712_sdca_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int mask_sft; + unsigned int val; + + if (item[0] >= e->items) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] == 0) + mask_sft = 12; + else if (ucontrol->value.enumerated.item[0] == 1) + mask_sft = 8; + else + return -EINVAL; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val); + val = (val >> mask_sft) & 0x3; + if (!val) + return 0; + + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3fff); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3 << mask_sft, 0); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return 1; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt712_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt712_sdca_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt712_adc23_enum, + rt712_sdca_mux_get, rt712_sdca_mux_put); + +static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_L), + unmute); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_R), + unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_L), + mute); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_R), + mute); + break; + } + return 0; +} + +static int rt712_sdca_fu0f_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt712->fu0f_dapm_mute = false; + rt712_sdca_set_fu0f_capture_ctl(rt712); + break; + case SND_SOC_DAPM_PRE_PMD: + rt712->fu0f_dapm_mute = true; + rt712_sdca_set_fu0f_capture_ctl(rt712); + break; + } + return 0; +} + +static int rt712_sdca_pde40_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + + default: + break; + } + + return 0; +} + +static const struct snd_kcontrol_new rt712_spk_sto_dac = + SOC_DAPM_DOUBLE_R("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_SUPPLY("PDE 40", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde40_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_fu05_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_fu0f_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = { + { "FU 05", NULL, "DP1RX" }, + { "DP4TX", NULL, "FU 0F" }, + + { "FU 0F", NULL, "PDE 12" }, + { "FU 0F", NULL, "ADC 23 Mux" }, + { "ADC 23 Mux", "LINE2", "LINE2" }, + { "ADC 23 Mux", "MIC2", "MIC2" }, + + { "HP", NULL, "PDE 40" }, + { "HP", NULL, "FU 05" }, +}; + +static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("FU06", SND_SOC_NOPM, 0, 0, &rt712_spk_sto_dac), + + /* Output */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt712_sdca_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = { + { "FU06", "Switch", "DP3RX" }, + { "CLASS D", NULL, "FU06" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt712->jd_src); + + return 0; +} + +static int rt712_sdca_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712_sdca_parse_dt(rt712, &rt712->slave->dev); + rt712->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + /* add SPK route */ + if (rt712->hw_id != RT712_DEV_ID_713) { + snd_soc_add_component_controls(component, + rt712_sdca_spk_controls, ARRAY_SIZE(rt712_sdca_spk_controls)); + snd_soc_dapm_new_controls(dapm, + rt712_sdca_spk_dapm_widgets, ARRAY_SIZE(rt712_sdca_spk_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt712_sdca_spk_dapm_routes, ARRAY_SIZE(rt712_sdca_spk_dapm_routes)); + } + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { + .probe = rt712_sdca_probe, + .controls = rt712_sdca_controls, + .num_controls = ARRAY_SIZE(rt712_sdca_controls), + .dapm_widgets = rt712_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dapm_widgets), + .dapm_routes = rt712_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt712_sdca_audio_map), + .set_jack = rt712_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + struct sdw_stream_data *stream; + + if (!sdw_stream) + return 0; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->sdw_stream = sdw_stream; + + /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ + snd_soc_dai_dma_data_set(dai, direction, stream); + + return 0; +} + +static void rt712_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_data *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(stream); +} + +static int rt712_sdca_pcm_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; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_data *stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!rt712->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT712_AIF1) + port = 1; + else if (dai->id == RT712_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT712_AIF1) + port = 4; + else + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt712->slave, &stream_config, + &port_config, 1, stream->sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT712_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT712_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT712_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT712_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + switch (dai->id) { + case RT712_AIF1: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; + case RT712_AIF2: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; + default: + dev_err(component->dev, "Wrong DAI id\n"); + return -EINVAL; + } + + return 0; +} + +static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt712->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt712->slave, stream->sdw_stream); + return 0; +} + +#define RT712_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) +#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt712_sdca_ops = { + .hw_params = rt712_sdca_pcm_hw_params, + .hw_free = rt712_sdca_pcm_hw_free, + .set_stream = rt712_sdca_set_sdw_stream, + .shutdown = rt712_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt712_sdca_dai[] = { + { + .name = "rt712-sdca-aif1", + .id = RT712_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + }, + { + .name = "rt712-sdca-aif2", + .id = RT712_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + } +}; + +int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712; + int ret; + + rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL); + if (!rt712) + return -ENOMEM; + + dev_set_drvdata(dev, rt712); + rt712->slave = slave; + rt712->regmap = regmap; + rt712->mbq_regmap = mbq_regmap; + + mutex_init(&rt712->calibrate_mutex); + mutex_init(&rt712->disable_irq_lock); + + INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt712->hw_init = false; + rt712->first_hw_init = false; + rt712->fu0f_dapm_mute = true; + rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true; + + /* JD source uses JD1 in default */ + rt712->jd_src = RT712_JD1; + + if (slave->id.part_id != RT712_PART_ID_713) + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712, rt712_sdca_dai, ARRAY_SIZE(rt712_sdca_dai)); + else + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712, rt712_sdca_dai, 1); + + dev_dbg(&slave->dev, "%s\n", __func__); + + return ret; +} + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val, hibernation_flag; + + rt712->disable_irq = false; + + if (rt712->hw_init) + return 0; + + if (rt712->first_hw_init) { + regcache_cache_only(rt712->regmap, false); + regcache_cache_bypass(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, false); + regcache_cache_bypass(rt712->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); + rt712->hw_id = (val & 0xf000) >> 12; + + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_ANALOG_CTL, RT712_MISC_POWER_CTL7, 0x0000); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + + /* calibration */ + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, &hibernation_flag); + if (!hibernation_flag) { + ret = rt712_sdca_calibration(rt712); + if (ret < 0) + dev_err(dev, "%s, calibration failed!\n", __func__); + } + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3000, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIC2_LINE2_PDE_FLOAT_CTL, 0x3412); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DAC03_HP_PDE_FLOAT_CTL, 0x4040); + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL, 0x0001, 0x0000); + regmap_write(rt712->regmap, 0x2f50, 0x00); + regmap_write(rt712->regmap, 0x2f54, 0x00); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + + /* add SPK settings */ + if (rt712->hw_id != RT712_DEV_ID_713) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_AMP_PDE_FLOAT_CTL, 0x2323); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); + } + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt712->hs_jack) + rt712_sdca_jack_init(rt712); + + if (!hibernation_flag) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); + + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, false); + regcache_mark_dirty(rt712->regmap); + regcache_cache_bypass(rt712->mbq_regmap, false); + regcache_mark_dirty(rt712->mbq_regmap); + } else + rt712->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt712->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h new file mode 100644 index 000000000000..cf647162f9da --- /dev/null +++ b/sound/soc/codecs/rt712-sdca.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt712-sdca.h -- RT712 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT712_H__ +#define __RT712_H__ + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt712_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + enum sdw_slave_status status; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct mutex calibrate_mutex; /* for headset calibration */ + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; + int jack_type; + int jd_src; + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + unsigned int hw_id; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; +}; + +struct sdw_stream_data { + struct sdw_stream_runtime *sdw_stream; +}; + +/* NID */ +#define RT712_VENDOR_REG 0x20 +#define RT712_VENDOR_CALI 0x58 +#define RT712_ULTRA_SOUND_DET 0x59 +#define RT712_VENDOR_IMS_DRE 0x5b +#define RT712_VENDOR_ANALOG_CTL 0x5f +#define RT712_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT712_JD_PRODUCT_NUM 0x00 +#define RT712_ANALOG_BIAS_CTL3 0x04 +#define RT712_LDO2_3_CTL1 0x0e +#define RT712_PARA_VERB_CTL 0x1a +#define RT712_CC_DET1 0x24 +#define RT712_COMBO_JACK_AUTO_CTL1 0x45 +#define RT712_COMBO_JACK_AUTO_CTL2 0x46 +#define RT712_COMBO_JACK_AUTO_CTL3 0x47 +#define RT712_DIGITAL_MISC_CTRL4 0x4a +#define RT712_FSM_CTL 0x67 +#define RT712_SW_CONFIG1 0x8a +#define RT712_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT712_DAC_DC_CALI_CTL1 0x00 +#define RT712_DAC_DC_CALI_CTL2 0x01 + +/* Index (NID:59h) */ +#define RT712_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT712_IMS_DIGITAL_CTL1 0x00 +#define RT712_IMS_DIGITAL_CTL5 0x05 +#define RT712_HP_DETECT_RLDET_CTL1 0x29 +#define RT712_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT712_MISC_POWER_CTL0 0x00 +#define RT712_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT712_HDA_LEGACY_MUX_CTL0 0x00 +#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT712_HDA_LEGACY_RESET_CTL 0x08 +#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT712_DMIC_ENT_FLOAT_CTL 0x10 +#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT712_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT712_ADC_ENT_FLOAT_CTL 0x15 +#define RT712_ADC_VOL_CH_FLOAT_CTL2 0x18 +#define RT712_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT712_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT712_ADC0A_08_PDE_FLOAT_CTL 0x26 +#define RT712_ADC0B_11_PDE_FLOAT_CTL 0x27 +#define RT712_DMIC1_2_PDE_FLOAT_CTL 0x2b +#define RT712_AMP_PDE_FLOAT_CTL 0x2c +#define RT712_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT712_GE_RELATED_CTL1 0x45 +#define RT712_GE_RELATED_CTL2 0x46 +#define RT712_MIXER_CTL0 0x52 +#define RT712_MIXER_CTL1 0x53 +#define RT712_EAPD_CTL 0x55 +#define RT712_UMP_HID_CTL0 0x60 +#define RT712_UMP_HID_CTL1 0x61 +#define RT712_UMP_HID_CTL2 0x62 +#define RT712_UMP_HID_CTL3 0x63 +#define RT712_UMP_HID_CTL4 0x64 +#define RT712_UMP_HID_CTL5 0x65 +#define RT712_UMP_HID_CTL6 0x66 +#define RT712_UMP_HID_CTL7 0x67 +#define RT712_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT712_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT712_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT712_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT712_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT712_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC DC offset calibration control-1 (0x00)(NID:58h) */ +#define RT712_DAC_DC_CALI_TRIGGER (0x1 << 15) + +#define RT712_EAPD_HIGH 0x2 +#define RT712_EAPD_LOW 0x0 + +/* RC Calibration register */ +#define RT712_RC_CAL 0x3201 + +/* Buffer address for HID */ +#define RT712_BUF_ADDR_HID1 0x44030000 +#define RT712_BUF_ADDR_HID2 0x44030020 + +/* RT712 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT712 SDCA entity */ +#define RT712_SDCA_ENT_HID01 0x01 +#define RT712_SDCA_ENT_GE49 0x49 +#define RT712_SDCA_ENT_USER_FU05 0x05 +#define RT712_SDCA_ENT_USER_FU06 0x06 +#define RT712_SDCA_ENT_USER_FU0F 0x0f +#define RT712_SDCA_ENT_USER_FU10 0x19 +#define RT712_SDCA_ENT_USER_FU1E 0x1e +#define RT712_SDCA_ENT_FU15 0x15 +#define RT712_SDCA_ENT_PDE23 0x23 +#define RT712_SDCA_ENT_PDE40 0x40 +#define RT712_SDCA_ENT_PDE11 0x11 +#define RT712_SDCA_ENT_PDE12 0x12 +#define RT712_SDCA_ENT_CS01 0x01 +#define RT712_SDCA_ENT_CS11 0x11 +#define RT712_SDCA_ENT_CS1F 0x1f +#define RT712_SDCA_ENT_CS1C 0x1c +#define RT712_SDCA_ENT_CS31 0x31 +#define RT712_SDCA_ENT_OT23 0x42 +#define RT712_SDCA_ENT_IT26 0x26 +#define RT712_SDCA_ENT_IT09 0x09 +#define RT712_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT712_SDCA_ENT_PLATFORM_FU44 0x44 + +/* RT712 SDCA control */ +#define RT712_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT712_SDCA_CTL_FU_MUTE 0x01 +#define RT712_SDCA_CTL_FU_VOLUME 0x02 +#define RT712_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT712_SDCA_CTL_SELECTED_MODE 0x01 +#define RT712_SDCA_CTL_DETECTED_MODE 0x02 +#define RT712_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT712_SDCA_CTL_VENDOR_DEF 0x30 +#define RT712_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT712 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 + +/* sample frequency index */ +#define RT712_SDCA_RATE_16000HZ 0x04 +#define RT712_SDCA_RATE_32000HZ 0x07 +#define RT712_SDCA_RATE_44100HZ 0x08 +#define RT712_SDCA_RATE_48000HZ 0x09 +#define RT712_SDCA_RATE_96000HZ 0x0b +#define RT712_SDCA_RATE_192000HZ 0x0d + +enum { + RT712_AIF1, + RT712_AIF2, +}; + +enum rt712_sdca_jd_src { + RT712_JD_NULL, + RT712_JD1, +}; + +enum rt712_sdca_hw_id { + RT712_DEV_ID_712 = 0x7, + RT712_DEV_ID_713 = 0x6, + RT712_DEV_ID_716 = 0x5, + RT712_DEV_ID_717 = 0x4, +}; + +#define RT712_PART_ID_713 0x713 + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); + +int rt712_sdca_jack_detect(struct rt712_sdca_priv *rt712, bool *hp, bool *mic); +#endif /* __RT712_H__ */ diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index c54ecf3e6987..38a82e4e2f95 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -172,7 +172,7 @@ static int rt715_sdca_read_prop(struct sdw_slave *slave) return 0; } -static struct sdw_slave_ops rt715_sdca_slave_ops = { +static const struct sdw_slave_ops rt715_sdca_slave_ops = { .read_prop = rt715_sdca_read_prop, .update_status = rt715_sdca_update_status, }; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 1fca7a3f46ea..920510365fd7 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -793,10 +793,7 @@ static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 1a2036ccfbac..c6dd9df7be45 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -777,10 +777,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index af52f2728854..62b02d764f09 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -57,10 +57,7 @@ static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c new file mode 100644 index 000000000000..727c01facf52 --- /dev/null +++ b/sound/soc/codecs/sma1303.c @@ -0,0 +1,1820 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// sma1303.c -- sma1303 ALSA SoC Audio driver +// +// Copyright 2023 Iron Device Corporation +// +// Auther: Gyuhwa Park <gyuhwa.park@irondevice.com> +// Kiseok Jo <kiseok.jo@irondevice.com> + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <asm/div64.h> + +#include "sma1303.h" + +#define CHECK_PERIOD_TIME 1 /* sec per HZ */ +#define MAX_CONTROL_NAME 48 + +#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\ + _post_n, _n, _vco, _p_cp)\ +{\ + .input_clk_name = _input_clk_name,\ + .output_clk_name = _output_clk_name,\ + .input_clk = _input_clk,\ + .post_n = _post_n,\ + .n = _n,\ + .vco = _vco,\ + .p_cp = _p_cp,\ +} + +enum sma1303_type { + SMA1303, +}; + +struct sma1303_pll_match { + char *input_clk_name; + char *output_clk_name; + unsigned int input_clk; + unsigned int post_n; + unsigned int n; + unsigned int vco; + unsigned int p_cp; +}; + +struct sma1303_priv { + enum sma1303_type devtype; + struct attribute_group *attr_grp; + struct delayed_work check_fault_work; + struct device *dev; + struct kobject *kobj; + struct regmap *regmap; + struct sma1303_pll_match *pll_matches; + bool amp_power_status; + bool force_mute_status; + int num_of_pll_matches; + int retry_cnt; + unsigned int amp_mode; + unsigned int cur_vol; + unsigned int format; + unsigned int frame_size; + unsigned int init_vol; + unsigned int last_bclk; + unsigned int last_ocp_val; + unsigned int last_over_temp; + unsigned int rev_num; + unsigned int sys_clk_id; + unsigned int tdm_slot_rx; + unsigned int tdm_slot_tx; + unsigned int tsdw_cnt; + long check_fault_period; + long check_fault_status; +}; + +static struct sma1303_pll_match sma1303_pll_matches[] = { +PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03), +PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03), +PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03), +PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07), +PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B), +PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A), +PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F), +}; + +static int sma1303_startup(struct snd_soc_component *); +static int sma1303_shutdown(struct snd_soc_component *); + +static const struct reg_default sma1303_reg_def[] = { + { 0x00, 0x80 }, + { 0x01, 0x00 }, + { 0x02, 0x00 }, + { 0x03, 0x11 }, + { 0x04, 0x17 }, + { 0x09, 0x00 }, + { 0x0A, 0x31 }, + { 0x0B, 0x98 }, + { 0x0C, 0x84 }, + { 0x0D, 0x07 }, + { 0x0E, 0x3F }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x14, 0x5C }, + { 0x15, 0x01 }, + { 0x16, 0x0F }, + { 0x17, 0x0F }, + { 0x18, 0x0F }, + { 0x19, 0x00 }, + { 0x1A, 0x00 }, + { 0x1B, 0x00 }, + { 0x23, 0x19 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x04 }, + { 0x33, 0x00 }, + { 0x36, 0x92 }, + { 0x37, 0x27 }, + { 0x3B, 0x5A }, + { 0x3C, 0x20 }, + { 0x3D, 0x00 }, + { 0x3E, 0x03 }, + { 0x3F, 0x0C }, + { 0x8B, 0x07 }, + { 0x8C, 0x70 }, + { 0x8D, 0x8B }, + { 0x8E, 0x6F }, + { 0x8F, 0x03 }, + { 0x90, 0x26 }, + { 0x91, 0x42 }, + { 0x92, 0xE0 }, + { 0x94, 0x35 }, + { 0x95, 0x0C }, + { 0x96, 0x42 }, + { 0x97, 0x95 }, + { 0xA0, 0x00 }, + { 0xA1, 0x3B }, + { 0xA2, 0xC8 }, + { 0xA3, 0x28 }, + { 0xA4, 0x40 }, + { 0xA5, 0x01 }, + { 0xA6, 0x41 }, + { 0xA7, 0x00 }, +}; + +static bool sma1303_readable_register(struct device *dev, unsigned int reg) +{ + bool result; + + if (reg > SMA1303_FF_DEVICE_INDEX) + return false; + + switch (reg) { + case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4: + case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL: + case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3: + case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7: + case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4: + case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1: + case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0: + case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2: + case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL: + case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4: + case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON: + case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2: + result = true; + break; + case SMA1303_FF_DEVICE_INDEX: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static bool sma1303_writeable_register(struct device *dev, unsigned int reg) +{ + bool result; + + if (reg > SMA1303_FF_DEVICE_INDEX) + return false; + + switch (reg) { + case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4: + case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL: + case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3: + case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7: + case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4: + case SMA1303_33_SDM_CTRL: + case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL: + case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2: + case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL: + case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4: + case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static bool sma1303_volatile_register(struct device *dev, unsigned int reg) +{ + bool result; + + switch (reg) { + case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2: + result = true; + break; + case SMA1303_FF_DEVICE_INDEX: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0); + +static int sma1303_regmap_write(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int val) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_write(sma1303->regmap, reg, val); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to write [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int mask, unsigned int val, bool *change) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_update_bits_check(sma1303->regmap, reg, + mask, val, change); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to update [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static int sma1303_regmap_read(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int *val) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_read(sma1303->regmap, reg, val); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to read [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static const char * const sma1303_aif_in_source_text[] = { + "Mono", "Left", "Right"}; +static const char * const sma1303_aif_out_source_text[] = { + "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post", + "Clk_PLL", "Clk_OSC"}; +static const char * const sma1303_tdm_slot_text[] = { + "Slot0", "Slot1", "Slot2", "Slot3", + "Slot4", "Slot5", "Slot6", "Slot7"}; + +static const struct soc_enum sma1303_aif_in_source_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text), + sma1303_aif_in_source_text); +static const struct soc_enum sma1303_aif_out_source_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text), + sma1303_aif_out_source_text); +static const struct soc_enum sma1303_tdm_slot_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_tdm_slot_text), + sma1303_tdm_slot_text); + +static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status; + dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__, + sma1303->force_mute_status ? "ON" : "OFF"); + + return 0; +} + +static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, val = (bool)ucontrol->value.integer.value[0]; + + if (sma1303->force_mute_status == val) + change = false; + else { + change = true; + sma1303->force_mute_status = val; + } + dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__, + sma1303->force_mute_status ? "ON" : "OFF"); + + return change; +} + +static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x7E) >> 1; + + return 0; +} + +static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_tdm_slot_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_A5_TDM1, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x38) >> 3; + sma1303->tdm_slot_rx = ucontrol->value.integer.value[0]; + + return 0; +} + +static int sma1303_tdm_slot_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, 0x38, (val << 3), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_tdm_slot_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_A6_TDM2, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x38) >> 3; + sma1303->tdm_slot_tx = ucontrol->value.integer.value[0]; + + return 0; +} + +static int sma1303_tdm_slot_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, 0x38, (val << 3), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_startup(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, temp = false; + + sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL, + SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp); + if (temp == true) + change = true; + + sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL, + SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp); + if (temp == true) + change = true; + + if (sma1303->amp_mode == SMA1303_MONO) { + sma1303_regmap_update_bits(sma1303, + SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, + SMA1303_SPK_MONO, + &temp); + if (temp == true) + change = true; + + } else { + sma1303_regmap_update_bits(sma1303, + SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, + SMA1303_SPK_STEREO, + &temp); + if (temp == true) + change = true; + } + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + sma1303->amp_power_status = true; + + return change; +} + +static int sma1303_shutdown(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, temp = false; + + cancel_delayed_work_sync(&sma1303->check_fault_work); + + sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp); + if (temp == true) + change = true; + + sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL, + SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp); + if (temp == true) + change = true; + sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL, + SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp); + if (temp == true) + change = true; + + sma1303->amp_power_status = false; + + return change; +} + +static int sma1303_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case 0: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_ON, + &change); + sma1303->amp_mode = SMA1303_MONO; + break; + case 1: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_OFF, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_LR_DATA_SW_MASK, + SMA1303_LR_DATA_SW_NORMAL, + &temp); + if (temp == true) + change = true; + sma1303->amp_mode = SMA1303_STEREO; + break; + case 2: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_OFF, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_LR_DATA_SW_MASK, + SMA1303_LR_DATA_SW_NORMAL, + &temp); + if (temp == true) + change = true; + sma1303->amp_mode = SMA1303_STEREO; + break; + default: + dev_err(sma1303->dev, "%s : Invalid value (%d)\n", + __func__, mux); + return -EINVAL; + } + + dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__, + sma1303_aif_in_source_text[mux]); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case 0: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_OUT_SEL_DISABLE, + &temp); + if (temp == true) + change = true; + break; + case 1: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_FORMAT_CONVERTER, + &temp); + if (temp == true) + change = true; + break; + case 2: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_MIXER_OUTPUT, + &temp); + if (temp == true) + change = true; + break; + case 3: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_SPEAKER_PATH, + &temp); + if (temp == true) + change = true; + break; + case 4: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_POSTSCALER_OUTPUT, + &temp); + if (temp == true) + change = true; + break; + case 5: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_CLK_OUT_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_MON_OSC_PLL_MASK, + SMA1303_PLL_SDO, + &temp); + if (temp == true) + change = true; + break; + case 6: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_CLK_OUT_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_MON_OSC_PLL_MASK, + SMA1303_OSC_SDO, + &temp); + if (temp == true) + change = true; + break; + default: + dev_err(sma1303->dev, "%s : Invalid value (%d)\n", + __func__, mux); + return -EINVAL; + } + + dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__, + sma1303_aif_out_source_text[mux]); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_sdo_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMU\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_CONFIG_MASK, + SMA1303_OUTPUT_PORT_ENABLE, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_SDO_OUTPUT_MASK, + SMA1303_NORMAL_OUT, + &temp); + if (temp == true) + change = true; + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMD\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_CONFIG_MASK, + SMA1303_INPUT_PORT_ONLY, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_SDO_OUTPUT_MASK, + SMA1303_HIGH_Z_OUT, + &temp); + if (temp == true) + change = true; + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_post_scaler_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + bool change = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMU\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, + SMA1303_BYP_POST_MASK, + SMA1303_EN_POST_SCALER, + &change); + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMD\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, + SMA1303_BYP_POST_MASK, + SMA1303_BYP_POST_SCALER, + &change); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMU\n", __func__); + ret = sma1303_startup(component); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMD\n", __func__); + ret = sma1303_shutdown(component); + break; + } + return ret; +} + +static const struct snd_kcontrol_new sma1303_aif_in_source_control = + SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum); +static const struct snd_kcontrol_new sma1303_aif_out_source_control = + SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum); +static const struct snd_kcontrol_new sma1303_sdo_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); +static const struct snd_kcontrol_new sma1303_post_scaler_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); +static const struct snd_kcontrol_new sma1303_enable_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new sma1303_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL, + 0, 167, 1, sma1303_spk_tlv), + SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0, + sma1303_force_mute_get, sma1303_force_mute_put), + SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0, + sma1303_postscaler_get, sma1303_postscaler_put), + SOC_ENUM_EXT("TDM RX Slot Position", sma1303_tdm_slot_enum, + sma1303_tdm_slot_rx_get, sma1303_tdm_slot_rx_put), + SOC_ENUM_EXT("TDM TX Slot Position", sma1303_tdm_slot_enum, + sma1303_tdm_slot_tx_get, sma1303_tdm_slot_tx_put), +}; + +static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = { + /* platform domain */ + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("SDO"), + + /* path domain */ + SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0, + &sma1303_aif_in_source_control, + sma1303_aif_in_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0, + &sma1303_aif_out_source_control, + sma1303_aif_out_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0, + &sma1303_sdo_control, + sma1303_sdo_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1, + &sma1303_post_scaler_control, + sma1303_post_scaler_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0, + sma1303_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, + &sma1303_enable_control), + + /* stream domain */ + SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route sma1303_audio_map[] = { + /* Playback */ + {"AIF IN Source", "Mono", "AIF IN"}, + {"AIF IN Source", "Left", "AIF IN"}, + {"AIF IN Source", "Right", "AIF IN"}, + + {"SDO Enable", "Switch", "AIF IN"}, + {"AIF OUT Source", "Disable", "SDO Enable"}, + {"AIF OUT Source", "After_FmtC", "SDO Enable"}, + {"AIF OUT Source", "After_Mixer", "SDO Enable"}, + {"AIF OUT Source", "After_DSP", "SDO Enable"}, + {"AIF OUT Source", "After_Post", "SDO Enable"}, + {"AIF OUT Source", "Clk_PLL", "SDO Enable"}, + {"AIF OUT Source", "Clk_OSC", "SDO Enable"}, + + {"Entry", NULL, "AIF OUT Source"}, + {"Entry", NULL, "AIF IN Source"}, + + {"Post Scaler", "Switch", "Entry"}, + {"AMP Power", NULL, "Entry"}, + {"AMP Power", NULL, "Entry"}, + + {"AMP Enable", "Switch", "AMP Power"}, + {"SPK", NULL, "AMP Enable"}, + + /* Capture */ + {"AIF OUT", NULL, "AMP Enable"}, +}; + +static int sma1303_setup_pll(struct snd_soc_component *component, + unsigned int bclk) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + int i = 0, ret = 0; + + dev_dbg(component->dev, "%s : BCLK = %dHz\n", + __func__, bclk); + + if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) { + dev_dbg(component->dev, "%s : MCLK is not supported\n", + __func__); + } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) { + for (i = 0; i < sma1303->num_of_pll_matches; i++) { + if (sma1303->pll_matches[i].input_clk == bclk) + break; + } + if (i == sma1303->num_of_pll_matches) { + dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n", + __func__); + return -EINVAL; + } + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK, + SMA1303_PLL_OPERATION|SMA1303_PLL_SCK, + NULL); + } + + ret += sma1303_regmap_write(sma1303, + SMA1303_8B_PLL_POST_N, + sma1303->pll_matches[i].post_n); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8C_PLL_N, + sma1303->pll_matches[i].n); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8D_PLL_A_SETTING, + sma1303->pll_matches[i].vco); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8F_PLL_P_CP, + sma1303->pll_matches[i].p_cp); + if (ret < 0) + return -EINVAL; + + return 0; +} + +static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int bclk = 0; + int ret = 0; + + if (sma1303->format == SND_SOC_DAIFMT_DSP_A) + bclk = params_rate(params) * sma1303->frame_size; + else + bclk = params_rate(params) * params_physical_width(params) + * params_channels(params); + + dev_dbg(component->dev, + "%s : rate = %d : bit size = %d : channel = %d\n", + __func__, params_rate(params), params_width(params), + params_channels(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) { + if (sma1303->last_bclk != bclk) { + sma1303_setup_pll(component, bclk); + sma1303->last_bclk = bclk; + } + } + + switch (params_rate(params)) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 44100: + case 48000: + case 96000: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_DAC_DN_CONV_MASK, + SMA1303_DAC_DN_CONV_DISABLE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_LOW_FIRST_CH, + NULL); + break; + + case 192000: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_DAC_DN_CONV_MASK, + SMA1303_DAC_DN_CONV_ENABLE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_HIGH_FIRST_CH, + NULL); + break; + + default: + dev_err(component->dev, "%s not support rate : %d\n", + __func__, params_rate(params)); + + return -EINVAL; + } + + } else { + + switch (params_format(params)) { + + case SNDRV_PCM_FORMAT_S16_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S16_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_32FS, + NULL); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S24_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_64FS, + NULL); + break; + case SNDRV_PCM_FORMAT_S32_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S32_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_64FS, + NULL); + break; + default: + dev_err(component->dev, + "%s not support data bit : %d\n", __func__, + params_format(params)); + return -EINVAL; + } + } + + switch (sma1303->format) { + case SND_SOC_DAIFMT_I2S: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_STANDARD_I2S, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_I2S, + NULL); + break; + case SND_SOC_DAIFMT_LEFT_J: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_LJ, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_LJ, + NULL); + break; + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 16: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_RJ_16BIT, + NULL); + break; + case 24: + case 32: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_RJ_24BIT, + NULL); + break; + } + break; + case SND_SOC_DAIFMT_DSP_A: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_STANDARD_I2S, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_TDM, + NULL); + break; + } + + switch (params_width(params)) { + case 16: + case 24: + case 32: + break; + default: + dev_err(component->dev, + "%s not support data bit : %d\n", __func__, + params_format(params)); + return -EINVAL; + } + if (ret < 0) + return -EINVAL; + + return 0; +} + +static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case SMA1303_EXTERNAL_CLOCK_19_2: + break; + case SMA1303_EXTERNAL_CLOCK_24_576: + break; + case SMA1303_PLL_CLKIN_MCLK: + break; + case SMA1303_PLL_CLKIN_BCLK: + break; + default: + dev_err(component->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + sma1303->sys_clk_id = clk_id; + return 0; +} + +static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (stream == SNDRV_PCM_STREAM_CAPTURE) + return ret; + + if (mute) { + dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE"); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_0E_MUTE_VOL_CTRL, + SMA1303_SPK_MUTE_MASK, + SMA1303_SPK_MUTE, + NULL); + + /* Need to wait time for mute slope */ + msleep(55); + } else { + if (!sma1303->force_mute_status) { + dev_dbg(component->dev, "%s : %s\n", + __func__, "UNMUTE"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_0E_MUTE_VOL_CTRL, + SMA1303_SPK_MUTE_MASK, + SMA1303_SPK_UNMUTE, + NULL); + } else { + dev_dbg(sma1303->dev, + "%s : FORCE MUTE!!!\n", __func__); + } + } + + if (ret < 0) + return -EINVAL; + return 0; +} + +static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + + case SND_SOC_DAIFMT_CBC_CFC: + dev_dbg(component->dev, + "%s : %s\n", __func__, "I2S/TDM Device mode"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_CONTROLLER_DEVICE_MASK, + SMA1303_DEVICE_MODE, + NULL); + break; + + case SND_SOC_DAIFMT_CBP_CFP: + dev_dbg(component->dev, + "%s : %s\n", __func__, "I2S/TDM Controller mode"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_CONTROLLER_DEVICE_MASK, + SMA1303_CONTROLLER_MODE, + NULL); + break; + + default: + dev_err(component->dev, + "Unsupported Controller/Device : 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + dev_err(component->dev, + "Unsupported Audio Interface Format : 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + + case SND_SOC_DAIFMT_IB_NF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Invert BCLK + Normal Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_SCK_RISING_MASK, + SMA1303_SCK_RISING_EDGE, + NULL); + break; + case SND_SOC_DAIFMT_IB_IF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Invert BCLK + Invert Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK, + SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE, + NULL); + break; + case SND_SOC_DAIFMT_NB_IF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Normal BCLK + Invert Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_HIGH_FIRST_CH, + NULL); + break; + case SND_SOC_DAIFMT_NB_NF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Normal BCLK + Normal Frame"); + break; + default: + dev_err(component->dev, + "Unsupported Bit & Frameclock : 0x%x\n", fmt); + return -EINVAL; + } + + if (ret < 0) + return -EINVAL; + return 0; +} + +static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n", + __func__, slots, slot_width); + + sma1303->frame_size = slot_width * slots; + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_TDM, + NULL); + + switch (slot_width) { + case 16: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_DL_MASK, + SMA1303_TDM_DL_16, + NULL); + break; + case 32: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_DL_MASK, + SMA1303_TDM_DL_32, + NULL); + break; + default: + dev_err(component->dev, "%s not support TDM %d slot_width\n", + __func__, slot_width); + break; + } + + switch (slots) { + case 4: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_N_SLOT_MASK, + SMA1303_TDM_N_SLOT_4, + NULL); + break; + case 8: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_N_SLOT_MASK, + SMA1303_TDM_N_SLOT_8, + NULL); + break; + default: + dev_err(component->dev, "%s not support TDM %d slots\n", + __func__, slots); + break; + } + + if (sma1303->tdm_slot_rx < slots) + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_SLOT1_RX_POS_MASK, + (sma1303->tdm_slot_rx) << 3, + NULL); + else + dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n", + __func__, sma1303->tdm_slot_rx); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_CLK_POL_MASK, + SMA1303_TDM_CLK_POL_RISE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_TX_MODE_MASK, + SMA1303_TDM_TX_MONO, + NULL); + + if (sma1303->tdm_slot_tx < slots) + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_SLOT1_TX_POS_MASK, + (sma1303->tdm_slot_tx) << 3, + NULL); + else + dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n", + __func__, sma1303->tdm_slot_tx); + + if (ret < 0) + return -EINVAL; + return 0; +} + +static const struct snd_soc_dai_ops sma1303_dai_ops_amp = { + .set_sysclk = sma1303_dai_set_sysclk_amp, + .set_fmt = sma1303_dai_set_fmt_amp, + .hw_params = sma1303_dai_hw_params_amp, + .mute_stream = sma1303_dai_mute, + .set_tdm_slot = sma1303_dai_set_tdm_slot, +}; + +#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000 +#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver sma1303_dai[] = { + { + .name = "sma1303-amplifier", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SMA1303_RATES, + .formats = SMA1303_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SMA1303_RATES, + .formats = SMA1303_FORMATS, + }, + .ops = &sma1303_dai_ops_amp, + }, +}; + +static void sma1303_check_fault_worker(struct work_struct *work) +{ + struct sma1303_priv *sma1303 = + container_of(work, struct sma1303_priv, check_fault_work.work); + int ret = 0; + unsigned int over_temp, ocp_val, uvlo_val; + + if (sma1303->tsdw_cnt) + ret = sma1303_regmap_read(sma1303, + SMA1303_0A_SPK_VOL, &sma1303->cur_vol); + else + ret = sma1303_regmap_read(sma1303, + SMA1303_0A_SPK_VOL, &sma1303->init_vol); + + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_0A_SPK_VOL : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FA_STATUS1 : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FB_STATUS2 : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret); + return; + } + + if (~over_temp & SMA1303_OT1_OK_STATUS) { + dev_crit(sma1303->dev, + "%s : OT1(Over Temperature Level 1)\n", __func__); + + if ((sma1303->cur_vol + 6) <= 0xFF) + sma1303_regmap_write(sma1303, + SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6); + + sma1303->tsdw_cnt++; + } else if (sma1303->tsdw_cnt) { + sma1303_regmap_write(sma1303, + SMA1303_0A_SPK_VOL, sma1303->init_vol); + sma1303->tsdw_cnt = 0; + sma1303->cur_vol = sma1303->init_vol; + } + + if (~over_temp & SMA1303_OT2_OK_STATUS) { + dev_crit(sma1303->dev, + "%s : OT2(Over Temperature Level 2)\n", __func__); + } + if (ocp_val & SMA1303_OCP_SPK_STATUS) { + dev_crit(sma1303->dev, + "%s : OCP_SPK(Over Current Protect SPK)\n", __func__); + } + if (ocp_val & SMA1303_OCP_BST_STATUS) { + dev_crit(sma1303->dev, + "%s : OCP_BST(Over Current Protect Boost)\n", __func__); + } + if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) { + dev_crit(sma1303->dev, + "%s : CLK_FAULT(No clock input)\n", __func__); + } + if (uvlo_val & SMA1303_UVLO_BST_STATUS) { + dev_crit(sma1303->dev, + "%s : UVLO(Under Voltage Lock Out)\n", __func__); + } + + if ((over_temp != sma1303->last_over_temp) || + (ocp_val != sma1303->last_ocp_val)) { + + dev_crit(sma1303->dev, "Please check AMP status"); + dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n", + over_temp, ocp_val); + sma1303->last_over_temp = over_temp; + sma1303->last_ocp_val = ocp_val; + } + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + if (!(~over_temp & SMA1303_OT1_OK_STATUS) + && !(~over_temp & SMA1303_OT2_OK_STATUS) + && !(ocp_val & SMA1303_OCP_SPK_STATUS) + && !(ocp_val & SMA1303_OCP_BST_STATUS) + && !(ocp_val & SMA1303_CLK_MON_STATUS) + && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) { + } +} + +static int sma1303_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static void sma1303_remove(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&sma1303->check_fault_work); +} + +static const struct snd_soc_component_driver sma1303_component = { + .probe = sma1303_probe, + .remove = sma1303_remove, + .controls = sma1303_snd_controls, + .num_controls = ARRAY_SIZE(sma1303_snd_controls), + .dapm_widgets = sma1303_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets), + .dapm_routes = sma1303_audio_map, + .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map), +}; + +const struct regmap_config sma_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SMA1303_FF_DEVICE_INDEX, + .readable_reg = sma1303_readable_register, + .writeable_reg = sma1303_writeable_register, + .volatile_reg = sma1303_volatile_register, + + .cache_type = REGCACHE_NONE, + .reg_defaults = sma1303_reg_def, + .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def), +}; + +static ssize_t check_fault_period_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period); +} + +static ssize_t check_fault_period_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + int ret; + + ret = kstrtol(buf, 10, &sma1303->check_fault_period); + + if (ret) + return -EINVAL; + + return (ssize_t)count; +} + +static DEVICE_ATTR_RW(check_fault_period); + +static ssize_t check_fault_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status); +} + +static ssize_t check_fault_status_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + int ret; + + ret = kstrtol(buf, 10, &sma1303->check_fault_status); + + if (ret) + return -EINVAL; + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + return (ssize_t)count; +} + +static DEVICE_ATTR_RW(check_fault_status); + +static struct attribute *sma1303_attr[] = { + &dev_attr_check_fault_period.attr, + &dev_attr_check_fault_status.attr, + NULL, +}; + +static struct attribute_group sma1303_attr_group = { + .attrs = sma1303_attr, +}; + +static int sma1303_i2c_probe(struct i2c_client *client) +{ + struct sma1303_priv *sma1303; + int ret, i = 0; + unsigned int device_info, status, otp_stat; + + sma1303 = devm_kzalloc(&client->dev, + sizeof(struct sma1303_priv), GFP_KERNEL); + if (!sma1303) + return -ENOMEM; + sma1303->dev = &client->dev; + + sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap); + if (IS_ERR(sma1303->regmap)) { + ret = PTR_ERR(sma1303->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", ret); + + return ret; + } + + ret = sma1303_regmap_read(sma1303, + SMA1303_FF_DEVICE_INDEX, &device_info); + + if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) { + dev_err(&client->dev, "device initialization error (%d 0x%02X)", + ret, device_info); + } + dev_dbg(&client->dev, "chip version 0x%02X\n", device_info); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_00_SYSTEM_CTRL, + SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET, + NULL); + + ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status); + sma1303->rev_num = status & SMA1303_REV_NUM_STATUS; + if (sma1303->rev_num == SMA1303_REV_NUM_TV0) + dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n"); + else if (sma1303->rev_num == SMA1303_REV_NUM_TV1) + dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n"); + + ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat); + if (ret < 0) + dev_err(&client->dev, + "failed to read, register: %02X, ret: %d\n", + SMA1303_FF_DEVICE_INDEX, ret); + + if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) && + ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) || + ((sma1303->rev_num != SMA1303_REV_NUM_TV0) && + ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1))) + dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n"); + else + dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n"); + + for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++) + ret += sma1303_regmap_write(sma1303, + sma1303_reg_def[i].reg, + sma1303_reg_def[i].def); + + sma1303->amp_mode = SMA1303_MONO; + sma1303->amp_power_status = false; + sma1303->check_fault_period = CHECK_PERIOD_TIME; + sma1303->check_fault_status = true; + sma1303->force_mute_status = false; + sma1303->init_vol = 0x31; + sma1303->cur_vol = sma1303->init_vol; + sma1303->last_bclk = 0; + sma1303->last_ocp_val = 0x08; + sma1303->last_over_temp = 0xC0; + sma1303->tsdw_cnt = 0; + sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT; + sma1303->tdm_slot_rx = 0; + sma1303->tdm_slot_tx = 0; + sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK; + + sma1303->dev = &client->dev; + sma1303->kobj = &client->dev.kobj; + + INIT_DELAYED_WORK(&sma1303->check_fault_work, + sma1303_check_fault_worker); + + i2c_set_clientdata(client, sma1303); + + sma1303->pll_matches = sma1303_pll_matches; + sma1303->num_of_pll_matches = + ARRAY_SIZE(sma1303_pll_matches); + + ret = devm_snd_soc_register_component(&client->dev, + &sma1303_component, sma1303_dai, 1); + if (ret) { + dev_err(&client->dev, "Failed to register component"); + + return ret; + } + + sma1303->attr_grp = &sma1303_attr_group; + ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp); + if (ret) { + dev_err(&client->dev, + "failed to create attribute group [%d]\n", ret); + sma1303->attr_grp = NULL; + } + + return ret; +} + +static void sma1303_i2c_remove(struct i2c_client *client) +{ + struct sma1303_priv *sma1303 = + (struct sma1303_priv *) i2c_get_clientdata(client); + + cancel_delayed_work_sync(&sma1303->check_fault_work); +} + +static const struct i2c_device_id sma1303_i2c_id[] = { + {"sma1303", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id); + +static const struct of_device_id sma1303_of_match[] = { + { .compatible = "irondevice,sma1303", }, + { } +}; +MODULE_DEVICE_TABLE(of, sma1303_of_match); + +static struct i2c_driver sma1303_i2c_driver = { + .driver = { + .name = "sma1303", + .of_match_table = sma1303_of_match, + }, + .probe_new = sma1303_i2c_probe, + .remove = sma1303_i2c_remove, + .id_table = sma1303_i2c_id, +}; + +module_i2c_driver(sma1303_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC SMA1303 driver"); +MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>"); +MODULE_AUTHOR("Kiseok Jo, <kiseok.jo@irondevice.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h new file mode 100644 index 000000000000..ae70f207adde --- /dev/null +++ b/sound/soc/codecs/sma1303.h @@ -0,0 +1,609 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * sma1303.h -- sma1303 ALSA SoC Audio driver + * + * Copyright 2023 Iron Device Corporation + * + * Author: Kiseok Jo <kiseok.jo@irondevice.com> + * + */ + +#ifndef _SMA1303_H +#define _SMA1303_H + +#define SMA1303_I2C_ADDR_00 0x1e +#define SMA1303_I2C_ADDR_01 0x3e +#define SMA1303_I2C_ADDR_10 0x5e +#define SMA1303_I2C_ADDR_11 0x7e + +#define SMA1303_EXTERNAL_CLOCK_19_2 0x00 +#define SMA1303_EXTERNAL_CLOCK_24_576 0x01 +#define SMA1303_PLL_CLKIN_MCLK 0x02 +#define SMA1303_PLL_CLKIN_BCLK 0x03 + +#define SMA1303_MONO 0x00 +#define SMA1303_STEREO 0x01 + +#define SMA1303_I2C_RETRY_COUNT 3 + +/* + * SMA1303 Register Definition + */ + +/* SMA1303 Register Addresses */ +#define SMA1303_00_SYSTEM_CTRL 0x00 +#define SMA1303_01_INPUT1_CTRL1 0x01 +#define SMA1303_02_INPUT1_CTRL2 0x02 +#define SMA1303_03_INPUT1_CTRL3 0x03 +#define SMA1303_04_INPUT1_CTRL4 0x04 +/* 0x05 ~ 0x08 : Reserved */ +#define SMA1303_09_OUTPUT_CTRL 0x09 +#define SMA1303_0A_SPK_VOL 0x0a +#define SMA1303_0B_BST_TEST 0x0b +#define SMA1303_0C_BST_TEST1 0x0c +#define SMA1303_0D_SPK_TEST 0x0d +#define SMA1303_0E_MUTE_VOL_CTRL 0x0e +/* 0x0F : Reserved */ +#define SMA1303_10_SYSTEM_CTRL1 0x10 +#define SMA1303_11_SYSTEM_CTRL2 0x11 +#define SMA1303_12_SYSTEM_CTRL3 0x12 +/* 0x13 : Reserved */ +#define SMA1303_14_MODULATOR 0x14 +#define SMA1303_15_BASS_SPK1 0x15 +#define SMA1303_16_BASS_SPK2 0x16 +#define SMA1303_17_BASS_SPK3 0x17 +#define SMA1303_18_BASS_SPK4 0x18 +#define SMA1303_19_BASS_SPK5 0x19 +#define SMA1303_1A_BASS_SPK6 0x1a +#define SMA1303_1B_BASS_SPK7 0x1b +/* 0x1C ~ 0x22 : Reserved */ +#define SMA1303_23_COMP_LIM1 0x23 +#define SMA1303_24_COMP_LIM2 0x24 +#define SMA1303_25_COMP_LIM3 0x25 +#define SMA1303_26_COMP_LIM4 0x26 +/* 0x27 ~ 0x32 : Reserved */ +#define SMA1303_33_SDM_CTRL 0x33 +#define SMA1303_34_OTP_DATA1 0x34 +/* 0x35 : Reserved */ +#define SMA1303_36_PROTECTION 0x36 +#define SMA1303_37_SLOPE_CTRL 0x37 +#define SMA1303_38_OTP_TRM0 0x38 +/* 0x39 ~ 0x3A : Reserved */ +#define SMA1303_3B_TEST1 0x3b +#define SMA1303_3C_TEST2 0x3c +#define SMA1303_3D_TEST3 0x3d +#define SMA1303_3E_ATEST1 0x3e +#define SMA1303_3F_ATEST2 0x3f +/* 0x40 ~ 0x8A : Reserved */ +#define SMA1303_8B_PLL_POST_N 0x8b +#define SMA1303_8C_PLL_N 0x8c +#define SMA1303_8D_PLL_A_SETTING 0x8d +#define SMA1303_8E_PLL_CTRL 0x8e +#define SMA1303_8F_PLL_P_CP 0x8f +#define SMA1303_90_POSTSCALER 0x90 +#define SMA1303_91_CLASS_G_CTRL 0x91 +#define SMA1303_92_FDPEC_CTRL 0x92 +/* 0x93 : Reserved */ +#define SMA1303_94_BOOST_CTRL1 0x94 +#define SMA1303_95_BOOST_CTRL2 0x95 +#define SMA1303_96_BOOST_CTRL3 0x96 +#define SMA1303_97_BOOST_CTRL4 0x97 +/* 0x98 ~ 0x9F : Reserved */ +#define SMA1303_A0_PAD_CTRL0 0xa0 +#define SMA1303_A1_PAD_CTRL1 0xa1 +#define SMA1303_A2_TOP_MAN1 0xa2 +#define SMA1303_A3_TOP_MAN2 0xa3 +#define SMA1303_A4_TOP_MAN3 0xa4 +#define SMA1303_A5_TDM1 0xa5 +#define SMA1303_A6_TDM2 0xa6 +#define SMA1303_A7_CLK_MON 0xa7 +/* 0xA8 ~ 0xF9 : Reserved */ +#define SMA1303_FA_STATUS1 0xfa +#define SMA1303_FB_STATUS2 0xfb +/* 0xFC ~ 0xFE : Reserved */ +#define SMA1303_FF_DEVICE_INDEX 0xff + +/* SMA1303 Registers Bit Fields */ + +/* SYSTEM_CTRL : 0x00 */ +#define SMA1303_RESETBYI2C_MASK (1<<1) +#define SMA1303_RESETBYI2C_NORMAL (0<<1) +#define SMA1303_RESETBYI2C_RESET (1<<1) + +#define SMA1303_POWER_MASK (1<<0) +#define SMA1303_POWER_OFF (0<<0) +#define SMA1303_POWER_ON (1<<0) + +/* INTPUT CTRL1 : 0x01 */ +#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7) +#define SMA1303_DEVICE_MODE (0<<7) +#define SMA1303_CONTROLLER_MODE (1<<7) + +#define SMA1303_I2S_MODE_MASK (7<<4) +#define SMA1303_STANDARD_I2S (0<<4) +#define SMA1303_LJ (1<<4) +#define SMA1303_RJ_16BIT (4<<4) +#define SMA1303_RJ_18BIT (5<<4) +#define SMA1303_RJ_20BIT (6<<4) +#define SMA1303_RJ_24BIT (7<<4) + +#define SMA1303_LEFTPOL_MASK (1<<3) +#define SMA1303_LOW_FIRST_CH (0<<3) +#define SMA1303_HIGH_FIRST_CH (1<<3) + +#define SMA1303_SCK_RISING_MASK (1<<2) +#define SMA1303_SCK_FALLING_EDGE (0<<2) +#define SMA1303_SCK_RISING_EDGE (1<<2) + +/* INTPUT CTRL2 : 0x02 */ +#define SMA1303_IMODE_MASK (3<<6) +#define SMA1303_I2S (0<<6) +#define SMA1303_PCM_SHORT (1<<6) +#define SMA1303_PCM_LONG (2<<6) + +#define RSMA1303_IGHT_FIRST_MASK (1<<5) +#define SMA1303_LEFT_NORMAL (0<<5) +#define SMA1303_RIGHT_INVERTED (1<<5) + +#define SMA1303_PCM_ALAW_MASK (1<<4) +#define SMA1303_PCM_U_DECODING (0<<4) +#define SMA1303_PCM_A_DECODING (1<<4) + +#define SMA1303_PCM_COMP_MASK (1<<3) +#define SMA1303_PCM_LINEAR (0<<3) +#define SMA1303_PCM_COMPANDING (1<<3) + +#define SMA1303_INPUTSEL_MASK (1<<2) +#define SMA1303_PCM_8KHZ (0<<2) +#define SMA1303_PCM_16KHZ (1<<2) + +#define SMA1303_PCM_STEREO_MASK (1<<1) +#define SMA1303_PCM_MONO (0<<1) +#define SMA1303_PCM_STEREO (1<<1) + +#define SMA1303_PCM_DL_MASK (1<<0) +#define SMA1303_PCM_8BIT (0<<0) +#define SMA1303_PCM_16BIT (1<<0) + +/* INTPUT CTRL3 : 0x03 */ +#define SMA1303_PCM_N_SLOT_MASK (15<<0) +#define SMA1303_PCM_N_SLOT1 (0<<0) +#define SMA1303_PCM_N_SLOT2 (1<<0) +#define SMA1303_PCM_N_SLOT3 (2<<0) +#define SMA1303_PCM_N_SLOT4 (3<<0) +#define SMA1303_PCM_N_SLOT5 (4<<0) +#define SMA1303_PCM_N_SLOT6 (5<<0) +#define SMA1303_PCM_N_SLOT7 (6<<0) +#define SMA1303_PCM_N_SLOT8 (7<<0) +#define SMA1303_PCM_N_SLOT9 (8<<0) +#define SMA1303_PCM_N_SLOT10 (9<<0) +#define SMA1303_PCM_N_SLOT11 (10<<0) +#define SMA1303_PCM_N_SLOT12 (11<<0) +#define SMA1303_PCM_N_SLOT13 (12<<0) +#define SMA1303_PCM_N_SLOT14 (13<<0) +#define SMA1303_PCM_N_SLOT15 (14<<0) +#define SMA1303_PCM_N_SLOT16 (15<<0) + +/* INTPUT CTRL4 : 0x04 */ +#define SMA1303_PCM1_SLOT_MASK (15<<4) +#define SMA1303_PCM1_SLOT1 (0<<4) +#define SMA1303_PCM1_SLOT2 (1<<4) +#define SMA1303_PCM1_SLOT3 (2<<4) +#define SMA1303_PCM1_SLOT4 (3<<4) +#define SMA1303_PCM1_SLOT5 (4<<4) +#define SMA1303_PCM1_SLOT6 (5<<4) +#define SMA1303_PCM1_SLOT7 (6<<4) +#define SMA1303_PCM1_SLOT8 (7<<4) +#define SMA1303_PCM1_SLOT9 (8<<4) +#define SMA1303_PCM1_SLOT10 (9<<4) +#define SMA1303_PCM1_SLOT11 (10<<4) +#define SMA1303_PCM1_SLOT12 (11<<4) +#define SMA1303_PCM1_SLOT13 (12<<4) +#define SMA1303_PCM1_SLOT14 (13<<4) +#define SMA1303_PCM1_SLOT15 (14<<4) +#define SMA1303_PCM1_SLOT16 (15<<4) + +#define SMA1303_PCM2_SLOT_MASK (15<<0) +#define SMA1303_PCM2_SLOT1 (0<<0) +#define SMA1303_PCM2_SLOT2 (1<<0) +#define SMA1303_PCM2_SLOT3 (2<<0) +#define SMA1303_PCM2_SLOT4 (3<<0) +#define SMA1303_PCM2_SLOT5 (4<<0) +#define SMA1303_PCM2_SLOT6 (5<<0) +#define SMA1303_PCM2_SLOT7 (6<<0) +#define SMA1303_PCM2_SLOT8 (7<<0) +#define SMA1303_PCM2_SLOT9 (8<<0) +#define SMA1303_PCM2_SLOT10 (9<<0) +#define SMA1303_PCM2_SLOT11 (10<<0) +#define SMA1303_PCM2_SLOT12 (11<<0) +#define SMA1303_PCM2_SLOT13 (12<<0) +#define SMA1303_PCM2_SLOT14 (13<<0) +#define SMA1303_PCM2_SLOT15 (14<<0) +#define SMA1303_PCM2_SLOT16 (15<<0) + +/* OUTPUT CTRL : 0x09 */ +#define SMA1303_PORT_CONFIG_MASK (3<<5) +#define SMA1303_INPUT_PORT_ONLY (0<<5) +#define SMA1303_OUTPUT_PORT_ENABLE (2<<5) + +#define SMA1303_PORT_OUT_SEL_MASK (7<<0) +#define SMA1303_OUT_SEL_DISABLE (0<<0) +#define SMA1303_FORMAT_CONVERTER (1<<0) +#define SMA1303_MIXER_OUTPUT (2<<0) +#define SMA1303_SPEAKER_PATH (3<<0) +#define SMA1303_POSTSCALER_OUTPUT (4<<0) + +/* BST_TEST : 0x0B */ +#define SMA1303_BST_OFF_SLOPE_MASK (3<<6) +#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6) +#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6) +#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6) +#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6) + +#define SMA1303_OCP_TEST_MASK (1<<5) +#define SMA1303_OCP_NORMAL_MODE (0<<5) +#define SMA1303_OCP_TEST_MODE (1<<5) + +#define SMA1303_BST_FAST_LEBN_MASK (1<<4) +#define SMA1303_BST_SHORT_LEB (0<<4) +#define SMA1303_BST_LONG_LEB (1<<4) + +#define SMA1303_HIGH_PGAIN_MASK (1<<3) +#define SMA1303_NORMAL_P_GAIN (0<<3) +#define SMA1303_HIGH_P_GAIN (1<<3) + +#define SMA1303_VCOMP_MASK (1<<2) +#define SMA1303_VCOMP_NORMAL_MODE (0<<2) +#define SMA1303_VCOMP_V_MON_MODE (1<<2) + +#define SMA1303_PMOS_ON_MASK (1<<1) +#define SMA1303_PMOS_NORMAL_MODE (0<<1) +#define SMA1303_PMOS_TEST_MODE (1<<1) + +#define SMA1303_NMOS_ON_MASK (1<<0) +#define SMA1303_NMOS_NORMAL_MODE (0<<0) +#define SMA1303_NMOS_TEST_MODE (1<<0) + +/* BST_TEST1 : 0x0C */ +#define SMA1303_SET_OCP_H_MASK (3<<6) +#define SMA1303_HIGH_OCP_4_5_LVL (0<<6) +#define SMA1303_HIGH_OCP_3_2_LVL (1<<6) +#define SMA1303_HIGH_OCP_2_1_LVL (2<<6) +#define SMA1303_HIGH_OCP_0_9_LVL (3<<6) + +#define SMA1303_OCL_TEST_MASK (1<<5) +#define SMA1303_OCL_NORMAL_MODE (0<<5) +#define SMA1303_OCL_TEST_MODE (1<<5) + +#define SMA1303_LOOP_CHECK_MASK (1<<4) +#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4) +#define SMA1303_BST_LOOP_CHECK_MODE (1<<4) + +#define SMA1303_EN_SH_PRT_MASK (1<<3) +#define SMA1303_EN_SH_PRT_DISABLE (0<<3) +#define SMA1303_EN_SH_PRT_ENABLE (1<<3) + +/* SPK_TEST : 0x0D */ +#define SMA1303_VREF_MON_MASK (1<<3) +#define SMA1303_VREF_NORMAL_MODE (0<<3) +#define SMA1303_VREF_V_MON_MODE (1<<3) + +#define SMA1303_SPK_OCP_DLYN_MASK (1<<2) +#define SMA1303_SPK_OCP_LONG_DELAY (0<<2) +#define SMA1303_SPK_OCP_NORMAL (1<<2) + +#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0) +#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0) +#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0) + +/* MUTE_VOL_CTRL : 0x0E */ +#define SMA1303_VOL_SLOPE_MASK (3<<6) +#define SMA1303_VOL_SLOPE_OFF (0<<6) +#define SMA1303_VOL_SLOPE_SLOW (1<<6) +#define SMA1303_VOL_SLOPE_MID (2<<6) +#define SMA1303_VOL_SLOPE_FAST (3<<6) + +#define SMA1303_MUTE_SLOPE_MASK (3<<4) +#define SMA1303_MUTE_SLOPE_OFF (0<<4) +#define SMA1303_MUTE_SLOPE_SLOW (1<<4) +#define SMA1303_MUTE_SLOPE_MID (2<<4) +#define SMA1303_MUTE_SLOPE_FAST (3<<4) + +#define SMA1303_SPK_MUTE_MASK (1<<0) +#define SMA1303_SPK_UNMUTE (0<<0) +#define SMA1303_SPK_MUTE (1<<0) + +/* SYSTEM_CTRL1 :0x10 */ +#define SMA1303_SPK_MODE_MASK (7<<2) +#define SMA1303_SPK_OFF (0<<2) +#define SMA1303_SPK_MONO (1<<2) +#define SMA1303_SPK_STEREO (4<<2) + +/* SYSTEM_CTRL2 : 0x11 */ +#define SMA1303_SPK_BS_MASK (1<<6) +#define SMA1303_SPK_BS_BYP (0<<6) +#define SMA1303_SPK_BS_EN (1<<6) +#define SMA1303_SPK_LIM_MASK (1<<5) +#define SMA1303_SPK_LIM_BYP (0<<5) +#define SMA1303_SPK_LIM_EN (1<<5) + +#define SMA1303_LR_DATA_SW_MASK (1<<4) +#define SMA1303_LR_DATA_SW_NORMAL (0<<4) +#define SMA1303_LR_DATA_SW_SWAP (1<<4) + +#define SMA1303_MONOMIX_MASK (1<<0) +#define SMA1303_MONOMIX_OFF (0<<0) +#define SMA1303_MONOMIX_ON (1<<0) + +/* SYSTEM_CTRL3 : 0x12 */ +#define SMA1303_INPUT_MASK (3<<6) +#define SMA1303_INPUT_0_DB (0<<6) +#define SMA1303_INPUT_M6_DB (1<<6) +#define SMA1303_INPUT_M12_DB (2<<6) +#define SMA1303_INPUT_INFI_DB (3<<6) +#define SMA1303_INPUT_R_MASK (3<<4) +#define SMA1303_INPUT_R_0_DB (0<<4) +#define SMA1303_INPUT_R_M6_DB (1<<4) +#define SMA1303_INPUT_R_M12_DB (2<<4) +#define SMA1303_INPUT_R_INFI_DB (3<<4) + +/* Modulator : 0x14 */ +#define SMA1303_SPK_HYSFB_MASK (3<<6) +#define SMA1303_HYSFB_625K (0<<6) +#define SMA1303_HYSFB_414K (1<<6) +#define SMA1303_HYSFB_297K (2<<6) +#define SMA1303_HYSFB_226K (3<<6) +#define SMA1303_SPK_BDELAY_MASK (63<<0) + +/* SDM CONTROL : 0x33 */ +#define SMA1303_SDM_Q_SEL_MASK (1<<2) +#define SMA1303_QUART_SEL_1_DIV_4 (0<<2) +#define SMA1303_QUART_SEL_1_DIV_8 (1<<2) + +/* OTP_DATA1 : 0x34 */ +#define SMA1303_OTP_LVL_MASK (1<<5) +#define SMA1303_OTP_LVL_NORMAL (0<<5) +#define SMA1303_OTP_LVL_LOW (1<<5) + +/* PROTECTION : 0x36 */ +#define SMA1303_EDGE_DIS_MASK (1<<7) +#define SMA1303_EDGE_DIS_ENABLE (0<<7) +#define SMA1303_EDGE_DIS_DISABLE (1<<7) + +#define SMA1303_SPK_OCP_DIS_MASK (1<<3) +#define SMA1303_SPK_OCP_ENABLE (0<<3) +#define SMA1303_SPK_OCP_DISABLE (1<<3) + +#define SMA1303_OCP_MODE_MASK (1<<2) +#define SMA1303_AUTO_RECOVER (0<<2) +#define SMA1303_SHUT_DOWN_PERMANENT (1<<2) + +#define SMA1303_OTP_MODE_MASK (3<<0) +#define SMA1303_OTP_MODE_DISABLE (0<<0) +#define SMA1303_IG_THR1_SHUT_THR2 (1<<0) +#define SMA1303_REC_THR1_SHUT_THR2 (2<<0) +#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0) + +/* TEST2 : 0x3C */ +#define SMA1303_SPK_HSDM_BP_MASK (1<<4) +#define SMA1303_SPK_HSDM_ENABLE (0<<4) +#define SMA1303_SPK_HSDM_BYPASS (1<<4) + +#define SMA1303_SDM_SYNC_DIS_MASK (1<<5) +#define SMA1303_SDM_SYNC_NORMAL (0<<5) +#define SMA1303_SDM_SYNC_DISABLE (1<<5) + +/* ATEST2 : 0x3F */ +#define SMA1303_SPK_OUT_FREQ_MASK (1<<2) +#define SMA1303_SPK_OUT_FREQ_360K (0<<2) +#define SMA1303_SPK_OUT_FREQ_410K (1<<2) + +#define SMA1303_LOW_POWER_MODE_MASK (1<<3) +#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3) +#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3) + +#define SMA1303_THERMAL_ADJUST_MASK (3<<5) +#define SMA1303_THERMAL_150_110 (0<<5) +#define SMA1303_THERMAL_160_120 (1<<5) +#define SMA1303_THERMAL_140_100 (2<<5) + +#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0) +#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0) +#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0) + +/* PLL_CTRL : 0x8E */ +#define SMA1303_TRM_LVL_MASK (1<<4) +#define SMA1303_TRM_LVL_NORMAL (0<<4) +#define SMA1303_TRM_LVL_LOW (1<<4) + +#define SMA1303_LOW_OCL_MODE_MASK (1<<3) +#define SMA1303_LOW_OCL_MODE (0<<3) +#define SMA1303_NORMAL_OCL_MODE (1<<3) + +#define SMA1303_PLL_PD2_MASK (7<<0) +#define SMA1303_PLL_PD2 (7<<0) +#define SMA1303_PLL_OPERATION2 (0<<0) + +/* POSTSCALER : 0x90 */ +#define SMA1303_BYP_POST_MASK (1<<0) +#define SMA1303_EN_POST_SCALER (0<<0) +#define SMA1303_BYP_POST_SCALER (1<<0) + +/* FDPEC CONTROL : 0x92 */ +#define SMA1303_FLT_VDD_GAIN_MASK (15<<4) +#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4) +#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4) +#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4) +#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4) +#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4) +#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4) +#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4) +#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4) +#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4) +#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4) +#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4) +#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4) +#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4) +#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4) +#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4) +#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4) + +#define SMA1303_DIS_FCHG_MASK (1<<2) +#define SMA1303_EN_FAST_CHARGE (0<<2) +#define SMA1303_DIS_FAST_CHARGE (1<<2) + +/* BOOST_CONTROL4 : 0x97 */ +#define SMA1303_TRM_VBST_MASK (7<<2) +#define SMA1303_TRM_VBST_5P5 (0<<2) +#define SMA1303_TRM_VBST_5P6 (1<<2) +#define SMA1303_TRM_VBST_5P7 (2<<2) +#define SMA1303_TRM_VBST_5P8 (3<<2) +#define SMA1303_TRM_VBST_5P9 (4<<2) +#define SMA1303_TRM_VBST_6P0 (5<<2) +#define SMA1303_TRM_VBST_6P1 (6<<2) +#define SMA1303_TRM_VBST_6P2 (7<<2) + +/* TOP_MAN1 : 0xA2 */ +#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7) +#define SMA1303_PLL_LOCK_ENABLE (0<<7) +#define SMA1303_PLL_LOCK_DISABLE (1<<7) + +#define SMA1303_PLL_PD_MASK (1<<6) +#define SMA1303_PLL_OPERATION (0<<6) +#define SMA1303_PLL_PD (1<<6) + +#define SMA1303_PLL_DIV_MASK (3<<4) +#define SMA1303_PLL_OUT (0<<4) +#define SMA1303_PLL_OUT_2 (1<<4) +#define SMA1303_PLL_OUT_4 (2<<4) +#define SMA1303_PLL_OUT_8 (3<<4) + +#define SMA1303_PLL_REF_CLK_MASK (1<<3) +#define SMA1303_PLL_REF_CLK1 (0<<3) +#define SMA1303_PLL_SCK (1<<3) + +#define SMA1303_DAC_DN_CONV_MASK (1<<2) +#define SMA1303_DAC_DN_CONV_DISABLE (0<<2) +#define SMA1303_DAC_DN_CONV_ENABLE (1<<2) + +#define SMA1303_SDO_IO_MASK (1<<1) +#define SMA1303_HIGH_Z_LRCK_H (0<<1) +#define SMA1303_HIGH_Z_LRCK_L (1<<1) + +#define SMA1303_SDO_OUTPUT2_MASK (1<<0) +#define SMA1303_SDO_NORMAL (0<<0) +#define SMA1303_SDO_OUTPUT_ONLY (1<<0) + +/* TOP_MAN2 : 0xA3 */ +#define SMA1303_MON_OSC_PLL_MASK (1<<7) +#define SMA1303_PLL_SDO (0<<7) +#define SMA1303_OSC_SDO (1<<7) + +#define SMA1303_TEST_CLKO_EN_MASK (1<<6) +#define SMA1303_NORMAL_SDO (0<<6) +#define SMA1303_CLK_OUT_SDO (1<<6) + +#define SMA1303_SDO_OUTPUT_MASK (1<<3) +#define SMA1303_NORMAL_OUT (0<<3) +#define SMA1303_HIGH_Z_OUT (1<<3) + +#define SMA1303_CLOCK_MON_MASK (1<<1) +#define SMA1303_CLOCK_MON (0<<1) +#define SMA1303_CLOCK_NOT_MON (1<<1) + +#define SMA1303_OSC_PD_MASK (1<<0) +#define SMA1303_NORMAL_OPERATION_OSC (0<<0) +#define SMA1303_POWER_DOWN_OSC (1<<0) + +/* TOP_MAN3 0xA4 */ +#define SMA1303_O_FORMAT_MASK (7<<5) +#define SMA1303_O_FMT_LJ (1<<5) +#define SMA1303_O_FMT_I2S (2<<5) +#define SMA1303_O_FMT_TDM (4<<5) + +#define SMA1303_SCK_RATE_MASK (1<<3) +#define SMA1303_SCK_64FS (0<<3) +#define SMA1303_SCK_32FS (2<<3) + +#define SMA1303_LRCK_POL_MASK (1<<0) +#define SMA1303_L_VALID (0<<0) +#define SMA1303_R_VALID (1<<0) + +/* TDM1 FORMAT : 0xA5 */ +#define SMA1303_TDM_CLK_POL_MASK (1<<7) +#define SMA1303_TDM_CLK_POL_RISE (0<<7) +#define SMA1303_TDM_CLK_POL_FALL (1<<7) + +#define SMA1303_TDM_TX_MODE_MASK (1<<6) +#define SMA1303_TDM_TX_MONO (0<<6) +#define SMA1303_TDM_TX_STEREO (1<<6) + +#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3) +#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3) +#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3) +#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3) +#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3) +#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3) +#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3) +#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3) +#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3) + +#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0) +#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0) +#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0) +#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0) +#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0) +#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0) +#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0) +#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0) +#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0) + +/* TDM2 FORMAT : 0xA6 */ +#define SMA1303_TDM_DL_MASK (1<<7) +#define SMA1303_TDM_DL_16 (0<<7) +#define SMA1303_TDM_DL_32 (1<<7) + +#define SMA1303_TDM_N_SLOT_MASK (1<<6) +#define SMA1303_TDM_N_SLOT_4 (0<<6) +#define SMA1303_TDM_N_SLOT_8 (1<<6) + +#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3) +#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3) +#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3) +#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3) +#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3) +#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3) +#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3) +#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3) +#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3) + +#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0) +#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0) +#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0) +#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0) +#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0) +#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0) +#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0) +#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0) +#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0) + +/* STATUS1 : 0xFA */ +#define SMA1303_OT1_OK_STATUS (1<<7) +#define SMA1303_OT2_OK_STATUS (1<<6) + +/* STATUS2 : 0xFB */ +#define SMA1303_OCP_SPK_STATUS (1<<5) +#define SMA1303_OCP_BST_STATUS (1<<4) +#define SMA1303_OTP_STAT_OK_0 (5<<1) +#define SMA1303_OTP_STAT_OK_1 (2<<2) + +#define SMA1303_CLK_MON_STATUS (1<<0) + +/* DEVICE_INFO : 0xFF */ +#define SMA1303_DEVICE_ID (2<<3) +#define SMA1303_UVLO_BST_STATUS (1<<2) +#define SMA1303_REV_NUM_STATUS (3<<0) +#define SMA1303_REV_NUM_TV0 (0<<0) +#define SMA1303_REV_NUM_TV1 (1<<0) + +#endif diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 3885c0bf0b01..de6d01c8fdd3 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -30,6 +30,7 @@ enum tas572x_type { TAS5720, + TAS5720A_Q1, TAS5722, }; @@ -166,17 +167,26 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - /* Enable manual TDM slot selection (instead of I2C ID based) */ - ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, - TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); - if (ret < 0) - goto error_snd_soc_component_update_bits; + /* + * Enable manual TDM slot selection (instead of I2C ID based). + * This is not applicable to TAS5720A-Q1. + */ + switch (tas5720->devtype) { + case TAS5720A_Q1: + break; + default: + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); + if (ret < 0) + goto error_snd_soc_component_update_bits; - /* Configure the TDM slot to process audio from */ - ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, - TAS5720_TDM_SLOT_SEL_MASK, first_slot); - if (ret < 0) - goto error_snd_soc_component_update_bits; + /* Configure the TDM slot to process audio from */ + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, + TAS5720_TDM_SLOT_SEL_MASK, first_slot); + if (ret < 0) + goto error_snd_soc_component_update_bits; + break; + } /* Configure TDM slot width. This is only applicable to TAS5722. */ switch (tas5720->devtype) { @@ -199,13 +209,24 @@ error_snd_soc_component_update_bits: return ret; } -static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) +static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute) { - struct snd_soc_component *component = dai->component; + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + unsigned int reg, mask; int ret; - ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, - TAS5720_MUTE, mute ? TAS5720_MUTE : 0); + switch (tas5720->devtype) { + case TAS5720A_Q1: + reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG; + mask = TAS5720_Q1_MUTE; + break; + default: + reg = TAS5720_DIGITAL_CTRL2_REG; + mask = TAS5720_MUTE; + break; + } + + ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0); if (ret < 0) { dev_err(component->dev, "error (un-)muting device: %d\n", ret); return ret; @@ -214,6 +235,11 @@ static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) return 0; } +static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + return tas5720_mute_soc_component(dai->component, mute); +} + static void tas5720_fault_check_work(struct work_struct *work) { struct tas5720_data *tas5720 = container_of(work, struct tas5720_data, @@ -305,6 +331,9 @@ static int tas5720_codec_probe(struct snd_soc_component *component) case TAS5720: expected_device_id = TAS5720_DEVICE_ID; break; + case TAS5720A_Q1: + expected_device_id = TAS5720A_Q1_DEVICE_ID; + break; case TAS5722: expected_device_id = TAS5722_DEVICE_ID; break; @@ -318,8 +347,20 @@ static int tas5720_codec_probe(struct snd_soc_component *component) expected_device_id, device_id); /* Set device to mute */ - ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, - TAS5720_MUTE, TAS5720_MUTE); + ret = tas5720_mute_soc_component(component, 1); + if (ret < 0) + goto error_snd_soc_component_update_bits; + + /* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */ + switch (tas5720->devtype) { + case TAS5720A_Q1: + ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG, + TAS5720_Q1_RESERVED7_BIT, + TAS5720_Q1_RESERVED7_BIT); + break; + default: + break; + } if (ret < 0) goto error_snd_soc_component_update_bits; @@ -471,6 +512,15 @@ static const struct regmap_config tas5720_regmap_config = { .volatile_reg = tas5720_is_volatile_reg, }; +static const struct regmap_config tas5720a_q1_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5720_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + static const struct regmap_config tas5722_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -492,6 +542,16 @@ static const DECLARE_TLV_DB_RANGE(dac_analog_tlv, ); /* + * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging + * from 19.2 dB to 25.0dB. + */ +static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1, + 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), + 0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0), + 0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0), +); + +/* * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps * depending on the device. Note that setting the gain below -100 dB * (register value <0x7) is effectively a MUTE as per device datasheet. @@ -537,6 +597,15 @@ static const struct snd_kcontrol_new tas5720_snd_controls[] = { TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), }; +static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = { + SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", + TAS5720_Q1_VOLUME_CTRL_LEFT_REG, + TAS5720_Q1_VOLUME_CTRL_RIGHT_REG, + 0, 0xff, 0, tas5720_dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1), +}; + static const struct snd_kcontrol_new tas5722_snd_controls[] = { SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume", 0, 0, 511, 0, @@ -574,6 +643,22 @@ static const struct snd_soc_component_driver soc_component_dev_tas5720 = { .endianness = 1, }; +static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + .controls = tas5720a_q1_snd_controls, + .num_controls = ARRAY_SIZE(tas5720a_q1_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + static const struct snd_soc_component_driver soc_component_dev_tas5722 = { .probe = tas5720_codec_probe, .remove = tas5720_codec_remove, @@ -633,6 +718,7 @@ static struct snd_soc_dai_driver tas5720_dai[] = { static const struct i2c_device_id tas5720_id[] = { { "tas5720", TAS5720 }, + { "tas5720a-q1", TAS5720A_Q1 }, { "tas5722", TAS5722 }, { } }; @@ -659,6 +745,9 @@ static int tas5720_probe(struct i2c_client *client) case TAS5720: regmap_config = &tas5720_regmap_config; break; + case TAS5720A_Q1: + regmap_config = &tas5720a_q1_regmap_config; + break; case TAS5722: regmap_config = &tas5722_regmap_config; break; @@ -692,6 +781,12 @@ static int tas5720_probe(struct i2c_client *client) tas5720_dai, ARRAY_SIZE(tas5720_dai)); break; + case TAS5720A_Q1: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5720_a_q1, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; case TAS5722: ret = devm_snd_soc_register_component(&client->dev, &soc_component_dev_tas5722, @@ -713,6 +808,7 @@ static int tas5720_probe(struct i2c_client *client) #if IS_ENABLED(CONFIG_OF) static const struct of_device_id tas5720_of_match[] = { { .compatible = "ti,tas5720", }, + { .compatible = "ti,tas5720a-q1", }, { .compatible = "ti,tas5722", }, { }, }; diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h index 223858f0de71..54b59b05ef0a 100644 --- a/sound/soc/codecs/tas5720.h +++ b/sound/soc/codecs/tas5720.h @@ -10,7 +10,7 @@ #ifndef __TAS5720_H__ #define __TAS5720_H__ -/* Register Address Map */ +/* Register Address Map - first 3 regs are common for all variants */ #define TAS5720_DEVICE_ID_REG 0x00 #define TAS5720_POWER_CTRL_REG 0x01 #define TAS5720_DIGITAL_CTRL1_REG 0x02 @@ -27,7 +27,13 @@ #define TAS5722_ANALOG_CTRL2_REG 0x14 #define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG +/* Register Address Map - volume controls for the TAS5720-Q1 variant */ +#define TAS5720_Q1_VOLUME_CTRL_CFG_REG 0x03 +#define TAS5720_Q1_VOLUME_CTRL_LEFT_REG 0x04 +#define TAS5720_Q1_VOLUME_CTRL_RIGHT_REG 0x05 + /* TAS5720_DEVICE_ID_REG */ +#define TAS5720A_Q1_DEVICE_ID 0x00 #define TAS5720_DEVICE_ID 0x01 #define TAS5722_DEVICE_ID 0x12 @@ -53,6 +59,10 @@ #define TAS5720_MUTE BIT(4) #define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) +/* TAS5720_Q1_VOLUME_CTRL_CFG_REG */ +#define TAS5720_Q1_FADE BIT(7) +#define TAS5720_Q1_MUTE GENMASK(1, 0) + /* TAS5720_ANALOG_CTRL_REG */ #define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4) #define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4) @@ -70,6 +80,10 @@ #define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2) #define TAS5720_ANALOG_GAIN_SHIFT (0x2) +/* TAS5720_Q1_ANALOG_CTRL_REG */ +#define TAS5720_Q1_RESERVED7_BIT BIT(7) +#define TAS5720_Q1_CHAN_SEL BIT(1) + /* TAS5720_FAULT_REG */ #define TAS5720_OC_THRESH_100PCT (0x0 << 4) #define TAS5720_OC_THRESH_75PCT (0x1 << 4) diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 91a22d927915..530f321d08e9 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -925,7 +925,7 @@ static int adcx140_configure_gpio(struct adcx140_priv *adcx140) gpio_count = device_property_count_u32(adcx140->dev, "ti,gpio-config"); - if (gpio_count == 0) + if (gpio_count <= 0) return 0; if (gpio_count != ADCX140_NUM_GPIO_CFGS) diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h index fd80fac8b327..27a1f1012fe2 100644 --- a/sound/soc/codecs/tlv320adcx140.h +++ b/sound/soc/codecs/tlv320adcx140.h @@ -6,7 +6,9 @@ #define _TLV320ADCX140_H #define ADCX140_RATES (SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000) + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) #define ADCX140_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 2305a472d132..5282112c7d8d 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -258,7 +258,25 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect); -static struct snd_soc_component_driver ts3a227e_soc_driver; +static int ts3a227e_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack == NULL) + return -EINVAL; + + return ts3a227e_enable_jack_detect(component, jack); +} + +static int ts3a227e_get_jack_type(struct snd_soc_component *component) +{ + return SND_JACK_HEADSET; +} + +static const struct snd_soc_component_driver ts3a227e_soc_driver = { + .name = "ti,ts3a227e", + .set_jack = ts3a227e_set_jack, + .get_jack_type = ts3a227e_get_jack_type, +}; static const struct regmap_config ts3a227e_regmap_config = { .val_bits = 8, diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 28175c746b9a..783479a4d535 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -4737,13 +4737,9 @@ static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp, if (dec_found && adc_mux_index <= 8) { tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); tx_stream_fs = snd_soc_component_read(comp, tx_fs_reg) & 0x0F; - if (tx_stream_fs <= 4) { - if (wcd->dmic_sample_rate <= - WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) - dmic_fs = wcd->dmic_sample_rate; - else - dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; - } else + if (tx_stream_fs <= 4) + dmic_fs = min(wcd->dmic_sample_rate, WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ); + else dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; } else { dmic_fs = wcd->dmic_sample_rate; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 1bf3c06a2b62..33d1b5ffeaeb 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -191,7 +191,7 @@ static int wcd9380_interrupt_callback(struct sdw_slave *slave, return IRQ_HANDLED; } -static struct sdw_slave_ops wcd9380_slave_ops = { +static const struct sdw_slave_ops wcd9380_slave_ops = { .update_status = wcd9380_update_status, .interrupt_callback = wcd9380_interrupt_callback, .bus_config = wcd9380_bus_config, diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 626278e4c923..737ca82cf976 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -484,11 +484,6 @@ static int wl1273_platform_probe(struct platform_device *pdev) &wl1273_dai, 1); } -static int wl1273_platform_remove(struct platform_device *pdev) -{ - return 0; -} - MODULE_ALIAS("platform:wl1273-codec"); static struct platform_driver wl1273_platform_driver = { @@ -496,7 +491,6 @@ static struct platform_driver wl1273_platform_driver = { .name = "wl1273-codec", }, .probe = wl1273_platform_probe, - .remove = wl1273_platform_remove, }; module_platform_driver(wl1273_platform_driver); diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8dac9fd88547..8eb4782c9232 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -37,7 +37,9 @@ #include "wm8940.h" struct wm8940_priv { - unsigned int sysclk; + unsigned int mclk; + unsigned int fs; + struct regmap *regmap; }; @@ -387,17 +389,24 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } +static int wm8940_update_clocks(struct snd_soc_dai *dai); static int wm8940_i2s_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; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(component); u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F; u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1; u16 companding = snd_soc_component_read(component, WM8940_COMPANDINGCTL) & 0xFFDF; int ret; + priv->fs = params_rate(params); + ret = wm8940_update_clocks(dai); + if (ret) + return ret; + /* LoutR control */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && params_channels(params) == 2) @@ -611,24 +620,6 @@ static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } -static int wm8940_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 wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component); - - switch (freq) { - case 11289600: - case 12000000: - case 12288000: - case 16934400: - case 18432000: - wm8940->sysclk = freq; - return 0; - } - return -EINVAL; -} - static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { @@ -653,6 +644,78 @@ static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return ret; } +static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out, + int *mclkdiv) +{ + unsigned int ratio = 2 * f_in / f_out; + + if (ratio <= 2) { + *mclkdiv = WM8940_MCLKDIV_1; + ratio = 2; + } else if (ratio == 3) { + *mclkdiv = WM8940_MCLKDIV_1_5; + } else if (ratio == 4) { + *mclkdiv = WM8940_MCLKDIV_2; + } else if (ratio <= 6) { + *mclkdiv = WM8940_MCLKDIV_3; + ratio = 6; + } else if (ratio <= 8) { + *mclkdiv = WM8940_MCLKDIV_4; + ratio = 8; + } else if (ratio <= 12) { + *mclkdiv = WM8940_MCLKDIV_6; + ratio = 12; + } else if (ratio <= 16) { + *mclkdiv = WM8940_MCLKDIV_8; + ratio = 16; + } else { + *mclkdiv = WM8940_MCLKDIV_12; + ratio = 24; + } + + return f_out * ratio / 2; +} + +static int wm8940_update_clocks(struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); + unsigned int fs256; + unsigned int fpll = 0; + unsigned int f; + int mclkdiv; + + if (!priv->mclk || !priv->fs) + return 0; + + fs256 = 256 * priv->fs; + + f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv); + if (f != priv->mclk) { + /* The PLL performs best around 90MHz */ + fpll = wm8940_get_mclkdiv(22500000, fs256, &mclkdiv); + } + + wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll); + wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv); + + return 0; +} + +static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *codec = dai->component; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + priv->mclk = freq; + + return wm8940_update_clocks(dai); +} + #define WM8940_RATES SNDRV_PCM_RATE_8000_48000 #define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -697,6 +760,17 @@ static int wm8940_probe(struct snd_soc_component *component) int ret; u16 reg; + /* + * Check chip ID for wm8940 - value of 0x00 offset + * SOFTWARE_RESET on write + * CHIP_ID on read + */ + reg = snd_soc_component_read(component, WM8940_SOFTRESET); + if (reg != WM8940_CHIP_ID) { + dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg); + return -ENODEV; + } + ret = wm8940_reset(component); if (ret < 0) { dev_err(component->dev, "Failed to issue reset\n"); @@ -709,9 +783,7 @@ static int wm8940_probe(struct snd_soc_component *component) if (ret < 0) return ret; - if (!pdata) - dev_warn(component->dev, "No platform data supplied\n"); - else { + if (pdata) { reg = snd_soc_component_read(component, WM8940_OUTPUTCTL); ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi); if (ret < 0) diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h index 0d4f53ada2e6..86bbf902ef5a 100644 --- a/sound/soc/codecs/wm8940.h +++ b/sound/soc/codecs/wm8940.h @@ -95,5 +95,8 @@ struct wm8940_setup_data { #define WM8940_OPCLKDIV_3 2 #define WM8940_OPCLKDIV_4 3 +/* Chip ID */ +#define WM8940_CHIP_ID 0x8940 + #endif /* _WM8940_H */ diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 6c8b1db649b8..f709231b1277 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -5,10 +5,7 @@ #include <linux/bitops.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> -#include <linux/interrupt.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/pm_runtime.h> @@ -424,7 +421,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = { } }; -static struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = { +static const struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = { { .num = 1, .ch_mask = 0x1, @@ -679,6 +676,11 @@ struct wsa881x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; + /* + * Logical state for SD_N GPIO: high for shutdown, low for enable. + * For backwards compatibility. + */ + unsigned int sd_n_val; int version; int active_ports; bool port_prepared[WSA881X_MAX_SWR_PORTS]; @@ -1101,7 +1103,7 @@ static int wsa881x_bus_config(struct sdw_slave *slave, return 0; } -static struct sdw_slave_ops wsa881x_slave_ops = { +static const struct sdw_slave_ops wsa881x_slave_ops = { .update_status = wsa881x_update_status, .bus_config = wsa881x_bus_config, .port_prep = wsa881x_port_prep, @@ -1113,20 +1115,39 @@ static int wsa881x_probe(struct sdw_slave *pdev, struct wsa881x_priv *wsa881x; struct device *dev = &pdev->dev; - wsa881x = devm_kzalloc(&pdev->dev, sizeof(*wsa881x), GFP_KERNEL); + wsa881x = devm_kzalloc(dev, sizeof(*wsa881x), GFP_KERNEL); if (!wsa881x) return -ENOMEM; - wsa881x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown", + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_FLAGS_BIT_NONEXCLUSIVE); - if (IS_ERR(wsa881x->sd_n)) { - dev_err(&pdev->dev, "Shutdown Control GPIO not found\n"); - return PTR_ERR(wsa881x->sd_n); - } + if (IS_ERR(wsa881x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), + "Shutdown Control GPIO not found\n"); - dev_set_drvdata(&pdev->dev, wsa881x); + /* + * Backwards compatibility work-around. + * + * The SD_N GPIO is active low, however upstream DTS used always active + * high. Changing the flag in driver and DTS will break backwards + * compatibility, so add a simple value inversion to work with both old + * and new DTS. + * + * This won't work properly with DTS using the flags properly in cases: + * 1. Old DTS with proper ACTIVE_LOW, however such case was broken + * before as the driver required the active high. + * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case + * (not existing upstream) but possible. This is the price of + * backwards compatibility, therefore this hack should be removed at + * some point. + */ + wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n); + if (!wsa881x->sd_n_val) + dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO."); + + dev_set_drvdata(dev, wsa881x); wsa881x->slave = pdev; - wsa881x->dev = &pdev->dev; + wsa881x->dev = dev; wsa881x->sconfig.ch_count = 1; wsa881x->sconfig.bps = 1; wsa881x->sconfig.frame_rate = 48000; @@ -1135,13 +1156,11 @@ static int wsa881x_probe(struct sdw_slave *pdev, pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); - if (IS_ERR(wsa881x->regmap)) { - dev_err(&pdev->dev, "regmap_init failed\n"); - return PTR_ERR(wsa881x->regmap); - } + if (IS_ERR(wsa881x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa881x->regmap), "regmap_init failed\n"); pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); @@ -1149,7 +1168,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, pm_runtime_set_active(dev); pm_runtime_enable(dev); - return devm_snd_soc_register_component(&pdev->dev, + return devm_snd_soc_register_component(dev, &wsa881x_component_drv, wsa881x_dais, ARRAY_SIZE(wsa881x_dais)); @@ -1160,7 +1179,7 @@ static int __maybe_unused wsa881x_runtime_suspend(struct device *dev) struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - gpiod_direction_output(wsa881x->sd_n, 0); + gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); @@ -1175,13 +1194,13 @@ static int __maybe_unused wsa881x_runtime_resume(struct device *dev) struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); unsigned long time; - gpiod_direction_output(wsa881x->sd_n, 1); + gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(WSA881X_PROBE_TIMEOUT)); if (!time) { dev_err(dev, "Initialization not complete, timed out\n"); - gpiod_direction_output(wsa881x->sd_n, 0); + gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); return -ETIMEDOUT; } diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 58fdb4e9fd97..c609cb63dae6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -4,16 +4,12 @@ */ #include <linux/bitops.h> -#include <linux/debugfs.h> -#include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_gpio.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/printk.h> #include <linux/regmap.h> @@ -522,7 +518,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = { } }; -static struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { +static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { { .num = 1, .ch_mask = 0x1, @@ -1073,7 +1069,7 @@ static int wsa883x_port_prep(struct sdw_slave *slave, return 0; } -static struct sdw_slave_ops wsa883x_slave_ops = { +static const struct sdw_slave_ops wsa883x_slave_ops = { .update_status = wsa883x_update_status, .port_prep = wsa883x_port_prep, }; @@ -1375,7 +1371,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, struct device *dev = &pdev->dev; int ret; - wsa883x = devm_kzalloc(&pdev->dev, sizeof(*wsa883x), GFP_KERNEL); + wsa883x = devm_kzalloc(dev, sizeof(*wsa883x), GFP_KERNEL); if (!wsa883x) return -ENOMEM; @@ -1388,17 +1384,17 @@ static int wsa883x_probe(struct sdw_slave *pdev, if (ret) return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n"); - wsa883x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown", + wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH); if (IS_ERR(wsa883x->sd_n)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(wsa883x->sd_n), + ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n), "Shutdown Control GPIO not found\n"); goto err; } - dev_set_drvdata(&pdev->dev, wsa883x); + dev_set_drvdata(dev, wsa883x); wsa883x->slave = pdev; - wsa883x->dev = &pdev->dev; + wsa883x->dev = dev; wsa883x->sconfig.ch_count = 1; wsa883x->sconfig.bps = 1; wsa883x->sconfig.direction = SDW_DATA_DIR_RX; @@ -1413,7 +1409,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config); if (IS_ERR(wsa883x->regmap)) { gpiod_direction_output(wsa883x->sd_n, 1); - ret = dev_err_probe(&pdev->dev, PTR_ERR(wsa883x->regmap), + ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap), "regmap_init failed\n"); goto err; } @@ -1423,7 +1419,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, pm_runtime_set_active(dev); pm_runtime_enable(dev); - ret = devm_snd_soc_register_component(&pdev->dev, + ret = devm_snd_soc_register_component(dev, &wsa883x_component_drv, wsa883x_dais, ARRAY_SIZE(wsa883x_dais)); diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 8d14b5593658..8099a829c304 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -297,7 +297,7 @@ SND_SOC_DAILINK_DEFS(hifi_be, DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_DUMMY())); -static struct snd_soc_dai_link fsl_asoc_card_dai[] = { +static const struct snd_soc_dai_link fsl_asoc_card_dai[] = { /* Default ASoC DAI Link*/ { .name = "HiFi", @@ -855,7 +855,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); if (ret) { - dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed: %d\n", ret); goto asrc_fail; } diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 35a52c3a020d..1b197478b3d9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -281,6 +281,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, val_cr4 |= FSL_SAI_CR4_MF; sai->is_pdm_mode = false; + sai->is_dsp_mode = false; /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -1459,14 +1460,17 @@ static int fsl_sai_probe(struct platform_device *pdev) if (sai->soc_data->use_imx_pcm) { ret = imx_pcm_dma_init(pdev); if (ret) { + dev_err_probe(dev, ret, "PCM DMA init failed\n"); if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)) dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n"); goto err_pm_get_sync; } } else { ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); - if (ret) + if (ret) { + dev_err_probe(dev, ret, "Registering PCM dmaengine failed\n"); goto err_pm_get_sync; + } } ret = devm_snd_soc_register_component(dev, &fsl_component, diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 2a6802fb2a8b..2a78243df752 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -21,6 +21,8 @@ struct fsl_xcvr_soc_data { const char *fw_name; + bool spdif_only; + bool use_edma; }; struct fsl_xcvr { @@ -261,6 +263,9 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) u32 i, div = 0, log2; int ret; + if (xcvr->soc_data->spdif_only) + return 0; + for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) { if (fsl_xcvr_pll_cfg[i].fout % freq == 0) { div = fsl_xcvr_pll_cfg[i].fout / freq; @@ -353,6 +358,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) struct device *dev = &xcvr->pdev->dev; int ret; + freq = xcvr->soc_data->spdif_only ? freq / 10 : freq; clk_disable_unprepare(xcvr->phy_clk); ret = clk_set_rate(xcvr->phy_clk, freq); if (ret < 0) { @@ -365,6 +371,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) return ret; } + if (xcvr->soc_data->spdif_only) + return 0; /* Release AI interface from reset */ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, FSL_XCVR_PHY_AI_CTRL_AI_RESETN); @@ -531,6 +539,16 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream, return -EBUSY; } + /* + * EDMA controller needs period size to be a multiple of + * tx/rx maxburst + */ + if (xcvr->soc_data->use_edma) + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + tx ? xcvr->dma_prms_tx.maxburst : + xcvr->dma_prms_rx.maxburst); + switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: case FSL_XCVR_MODE_ARC: @@ -547,10 +565,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream, xcvr->streams |= BIT(substream->stream); - /* Disable XCVR controls if there is stream started */ - fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false); - fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false); - fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false); + if (!xcvr->soc_data->spdif_only) { + /* Disable XCVR controls if there is stream started */ + fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false); + } return 0; } @@ -567,12 +587,13 @@ static void fsl_xcvr_shutdown(struct snd_pcm_substream *substream, /* Enable XCVR controls if there is no stream started */ if (!xcvr->streams) { - fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true); - fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, - (xcvr->mode == FSL_XCVR_MODE_ARC)); - fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, - (xcvr->mode == FSL_XCVR_MODE_EARC)); - + if (!xcvr->soc_data->spdif_only) { + fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_ARC)); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_EARC)); + } ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, FSL_XCVR_IRQ_EARC_ALL, 0); if (ret < 0) { @@ -673,7 +694,10 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); return ret; } - fallthrough; + if (xcvr->soc_data->spdif_only) + break; + else + fallthrough; case FSL_XCVR_MODE_EARC: /* clear ISR_CMDC_TX_EN, W1C */ ret = regmap_write(xcvr->regmap, @@ -877,9 +901,13 @@ static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &xcvr->dma_prms_tx, &xcvr->dma_prms_rx); - snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1); - snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1); - snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1); + if (xcvr->soc_data->spdif_only) + xcvr->mode = FSL_XCVR_MODE_SPDIF; + else { + snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1); + snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1); + snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1); + } snd_soc_add_dai_controls(dai, fsl_xcvr_tx_ctls, ARRAY_SIZE(fsl_xcvr_tx_ctls)); snd_soc_add_dai_controls(dai, fsl_xcvr_rx_ctls, @@ -930,10 +958,11 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = { { FSL_XCVR_ISR_SET, 0x00000000 }, { FSL_XCVR_ISR_CLR, 0x00000000 }, { FSL_XCVR_ISR_TOG, 0x00000000 }, - { FSL_XCVR_RX_DPTH_CTRL, 0x00002C89 }, - { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00002C89 }, - { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00002C89 }, - { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00002C89 }, + { FSL_XCVR_CLK_CTRL, 0x0000018F }, + { FSL_XCVR_RX_DPTH_CTRL, 0x00040CC1 }, + { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00040CC1 }, + { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00040CC1 }, + { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00040CC1 }, { FSL_XCVR_RX_DPTH_CNTR_CTRL, 0x00000000 }, { FSL_XCVR_RX_DPTH_CNTR_CTRL_SET, 0x00000000 }, { FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR, 0x00000000 }, @@ -966,6 +995,12 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = { static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) { + struct fsl_xcvr *xcvr = dev_get_drvdata(dev); + + if (xcvr->soc_data->spdif_only) + if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) || + reg > FSL_XCVR_TX_DPTH_BCRR) + return false; switch (reg) { case FSL_XCVR_VERSION: case FSL_XCVR_EXT_CTRL: @@ -991,6 +1026,12 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) case FSL_XCVR_RX_DPTH_CTRL_SET: case FSL_XCVR_RX_DPTH_CTRL_CLR: case FSL_XCVR_RX_DPTH_CTRL_TOG: + case FSL_XCVR_RX_CS_DATA_0: + case FSL_XCVR_RX_CS_DATA_1: + case FSL_XCVR_RX_CS_DATA_2: + case FSL_XCVR_RX_CS_DATA_3: + case FSL_XCVR_RX_CS_DATA_4: + case FSL_XCVR_RX_CS_DATA_5: case FSL_XCVR_RX_DPTH_CNTR_CTRL: case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET: case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR: @@ -1027,6 +1068,11 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) { + struct fsl_xcvr *xcvr = dev_get_drvdata(dev); + + if (xcvr->soc_data->spdif_only) + if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) + return false; switch (reg) { case FSL_XCVR_EXT_CTRL: case FSL_XCVR_EXT_IER0: @@ -1103,32 +1149,34 @@ static irqreturn_t irq0_isr(int irq, void *devid) if (isr & FSL_XCVR_IRQ_NEW_CS) { dev_dbg(dev, "Received new CS block\n"); isr_clr |= FSL_XCVR_IRQ_NEW_CS; - /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */ - regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, - FSL_XCVR_EXT_CTRL_PAGE_MASK, - FSL_XCVR_EXT_CTRL_PAGE(8)); - - /* Find updated CS buffer */ - reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0; - reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0; - memcpy_fromio(&val, reg_ctrl, sizeof(val)); - if (!val) { - reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1; - reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1; + if (!xcvr->soc_data->spdif_only) { + /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */ + regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_PAGE_MASK, + FSL_XCVR_EXT_CTRL_PAGE(8)); + + /* Find updated CS buffer */ + reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0; + reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0; memcpy_fromio(&val, reg_ctrl, sizeof(val)); - } + if (!val) { + reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1; + reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1; + memcpy_fromio(&val, reg_ctrl, sizeof(val)); + } - if (val) { - /* copy CS buffer */ - memcpy_fromio(&xcvr->rx_iec958.status, reg_buff, - sizeof(xcvr->rx_iec958.status)); - for (i = 0; i < 6; i++) { - val = *(u32 *)(xcvr->rx_iec958.status + i*4); - *(u32 *)(xcvr->rx_iec958.status + i*4) = - bitrev32(val); + if (val) { + /* copy CS buffer */ + memcpy_fromio(&xcvr->rx_iec958.status, reg_buff, + sizeof(xcvr->rx_iec958.status)); + for (i = 0; i < 6; i++) { + val = *(u32 *)(xcvr->rx_iec958.status + i*4); + *(u32 *)(xcvr->rx_iec958.status + i*4) = + bitrev32(val); + } + /* clear CS control register */ + memset_io(reg_ctrl, 0, sizeof(val)); } - /* clear CS control register */ - memset_io(reg_ctrl, 0, sizeof(val)); } } if (isr & FSL_XCVR_IRQ_NEW_UD) { @@ -1168,8 +1216,14 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = { .fw_name = "imx/xcvr/xcvr-imx8mp.bin", }; +static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { + .spdif_only = true, + .use_edma = true, +}; + static const struct of_device_id fsl_xcvr_dt_ids[] = { { .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data }, + { .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids); @@ -1229,7 +1283,7 @@ static int fsl_xcvr_probe(struct platform_device *pdev) return PTR_ERR(xcvr->regmap); } - xcvr->reset = devm_reset_control_get_exclusive(dev, NULL); + xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(xcvr->reset)) { dev_err(dev, "failed to get XCVR reset control\n"); return PTR_ERR(xcvr->reset); @@ -1306,12 +1360,14 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) if (ret < 0) dev_err(dev, "Failed to clear IER0: %d\n", ret); - /* Assert M0+ reset */ - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, - FSL_XCVR_EXT_CTRL_CORE_RESET, - FSL_XCVR_EXT_CTRL_CORE_RESET); - if (ret < 0) - dev_err(dev, "Failed to assert M0+ core: %d\n", ret); + if (!xcvr->soc_data->spdif_only) { + /* Assert M0+ reset */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_CORE_RESET, + FSL_XCVR_EXT_CTRL_CORE_RESET); + if (ret < 0) + dev_err(dev, "Failed to assert M0+ core: %d\n", ret); + } regcache_cache_only(xcvr->regmap, true); @@ -1367,6 +1423,9 @@ static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev) goto stop_spba_clk; } + if (xcvr->soc_data->spdif_only) + return 0; + ret = reset_control_deassert(xcvr->reset); if (ret) { dev_err(dev, "failed to deassert M0+ reset.\n"); diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h index 4769b0fca21d..044058fc6aa2 100644 --- a/sound/soc/fsl/fsl_xcvr.h +++ b/sound/soc/fsl/fsl_xcvr.h @@ -49,6 +49,13 @@ #define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188 #define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c +#define FSL_XCVR_RX_CS_DATA_0 0x190 +#define FSL_XCVR_RX_CS_DATA_1 0x194 +#define FSL_XCVR_RX_CS_DATA_2 0x198 +#define FSL_XCVR_RX_CS_DATA_3 0x19C +#define FSL_XCVR_RX_CS_DATA_4 0x1A0 +#define FSL_XCVR_RX_CS_DATA_5 0x1A4 + #define FSL_XCVR_RX_DPTH_CNTR_CTRL 0x1C0 #define FSL_XCVR_RX_DPTH_CNTR_CTRL_SET 0x1C4 #define FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR 0x1C8 diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c index a780cf5a65ff..b6cc7e6c2a32 100644 --- a/sound/soc/fsl/imx-hdmi.c +++ b/sound/soc/fsl/imx-hdmi.c @@ -202,7 +202,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&data->card, data); ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); goto fail; } diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 2f310994f7ee..6614b3447649 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -140,7 +140,6 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component, { struct rpmsg_info *info = dev_get_drvdata(component->dev); struct rpmsg_msg *msg; - int ret = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { msg = &info->msg[TX_HW_PARAM]; @@ -184,7 +183,7 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component, info->send_message(msg, info); - return ret; + return 0; } static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component, @@ -282,7 +281,6 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct rpmsg_info *info = dev_get_drvdata(component->dev); struct rpmsg_msg *msg; - int ret = 0; /* Flush work in workqueue to make TX_CLOSE is the last message */ flush_workqueue(info->rpmsg_wq); @@ -305,7 +303,7 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component, dev_warn(rtd->dev, "Msg is dropped!, number is %d\n", info->msg_drop_count[substream->stream]); - return ret; + return 0; } static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index e35becce9635..56552a616f21 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -786,6 +786,55 @@ int asoc_simple_init_jack(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_init_jack); +int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct snd_soc_component *component; + int found_jack_index = 0; + int type = 0; + int num = 0; + int ret; + + if (priv->aux_jacks) + return 0; + + for_each_card_auxs(card, component) { + type = snd_soc_component_get_jack_type(component); + if (type > 0) + num++; + } + if (num < 1) + return 0; + + priv->aux_jacks = devm_kcalloc(card->dev, num, + sizeof(struct snd_soc_jack), GFP_KERNEL); + if (!priv->aux_jacks) + return -ENOMEM; + + for_each_card_auxs(card, component) { + char id[128]; + struct snd_soc_jack *jack; + + if (found_jack_index >= num) + break; + + type = snd_soc_component_get_jack_type(component); + if (type <= 0) + continue; + + /* create jack */ + jack = &(priv->aux_jacks[found_jack_index++]); + snprintf(id, sizeof(id), "%s-jack", component->name); + ret = snd_soc_card_jack_new(card, id, type, jack); + if (ret) + continue; + + (void)snd_soc_component_set_jack(component, jack, NULL); + } + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks); + int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct link_info *li) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index feb55b66239b..e98932c16754 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -623,6 +623,10 @@ static int simple_soc_probe(struct snd_soc_card *card) if (ret < 0) return ret; + ret = asoc_simple_init_aux_jacks(priv, PREFIX); + if (ret < 0) + return ret; + return 0; } diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index fd59b35a62ba..38116c758717 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1327,15 +1327,13 @@ static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) { struct sst_data *drv = snd_soc_dai_get_drvdata(dai); - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct snd_soc_dapm_path *p; dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); + dev_dbg(dai->dev, "Stream name=%s\n", w->name); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev_dbg(dai->dev, "Stream name=%s\n", - dai->playback_widget->name); - w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; @@ -1352,9 +1350,6 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) } } } else { - dev_dbg(dai->dev, "Stream name=%s\n", - dai->capture_widget->name); - w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connected && !p->connected(w, p->source)) continue; diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 1c6924a1ebca..460ee6599daf 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ - topology.o path.o pcm.o board_selection.o + topology.o path.o pcm.o board_selection.o control.o snd-soc-avs-objs += cldma.o snd-soc-avs-objs += skl.o apl.o diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index 6731d8a49076..b31fa931ba8b 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -258,14 +258,15 @@ static int avs_card_resume_post(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); + int stream = SNDRV_PCM_STREAM_PLAYBACK; if (!codec_dai) { dev_err(card->dev, "Codec dai not found\n"); return -EINVAL; } - if (codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] && - codec_dai->playback_widget->active) + if (snd_soc_dai_stream_active(codec_dai, stream) && + snd_soc_dai_get_widget(codec_dai, stream)->active) snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN); return snd_soc_component_set_jack(codec_dai->component, jack, NULL); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 8447b37a2a41..3551a05bd599 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -98,7 +98,7 @@ static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc static int avs_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *runtime = substream->private_data; + struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); int ret; diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index bd25f0fde35e..2923f3805bbe 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -109,7 +109,7 @@ static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc static int avs_rt298_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_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int clk_freq; int ret; diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c new file mode 100644 index 000000000000..a8b14b784f8a --- /dev/null +++ b/sound/soc/intel/avs/control.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <sound/soc.h> +#include "avs.h" +#include "control.h" +#include "messages.h" +#include "path.h" + +static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget *w; + + w = snd_soc_dapm_kcontrol_widget(kcontrol); + + return to_avs_dev(w->dapm->component->dev); +} + +static struct avs_path_module *avs_get_kcontrol_module(struct avs_dev *adev, u32 id) +{ + struct avs_path *path; + struct avs_path_pipeline *ppl; + struct avs_path_module *mod; + + list_for_each_entry(path, &adev->path_list, node) + list_for_each_entry(ppl, &path->ppl_list, node) + list_for_each_entry(mod, &ppl->mod_list, node) + if (mod->template->ctl_id && mod->template->ctl_id == id) + return mod; + + return NULL; +} + +int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; + struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); + struct avs_volume_cfg *dspvols = NULL; + struct avs_path_module *active_module; + size_t num_dspvols; + int ret = 0; + + /* prevent access to modules while path is being constructed */ + mutex_lock(&adev->path_mutex); + + active_module = avs_get_kcontrol_module(adev, ctl_data->id); + if (active_module) { + ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id, + active_module->instance_id, &dspvols, + &num_dspvols); + if (!ret) + ucontrol->value.integer.value[0] = dspvols[0].target_volume; + + ret = AVS_IPC_RET(ret); + kfree(dspvols); + } else { + ucontrol->value.integer.value[0] = ctl_data->volume; + } + + mutex_unlock(&adev->path_mutex); + return ret; +} + +int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; + struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); + long *volume = &ctl_data->volume; + struct avs_path_module *active_module; + struct avs_volume_cfg dspvol = {0}; + long ctlvol = ucontrol->value.integer.value[0]; + int ret = 0, changed = 0; + + if (ctlvol < 0 || ctlvol > mc->max) + return -EINVAL; + + /* prevent access to modules while path is being constructed */ + mutex_lock(&adev->path_mutex); + + if (*volume != ctlvol) { + *volume = ctlvol; + changed = 1; + } + + active_module = avs_get_kcontrol_module(adev, ctl_data->id); + if (active_module) { + dspvol.channel_id = AVS_ALL_CHANNELS_MASK; + dspvol.target_volume = *volume; + + ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id, + active_module->instance_id, &dspvol); + ret = AVS_IPC_RET(ret); + } + + mutex_unlock(&adev->path_mutex); + + return ret ? ret : changed; +} diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h new file mode 100644 index 000000000000..08631bde13c3 --- /dev/null +++ b/sound/soc/intel/avs/control.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + * Cezary Rojewski <cezary.rojewski@intel.com> + */ + +#ifndef __SOUND_SOC_INTEL_AVS_CTRL_H +#define __SOUND_SOC_INTEL_AVS_CTRL_H + +#include <sound/control.h> + +struct avs_control_data { + u32 id; + + long volume; +}; + +int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +#endif diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index e11ae4246416..f887ab5b0311 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -702,6 +702,35 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, (u8 *)&cpr_fmt, sizeof(cpr_fmt)); } +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, (u8 *)vol, + sizeof(*vol)); +} + +int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg **vols, size_t *num_vols) +{ + size_t payload_size; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + /* Non-zero payload expected for PEAKVOL_VOLUME. */ + if (!payload_size) + return -EREMOTEIO; + + *vols = (struct avs_volume_cfg *)payload; + *num_vols = payload_size / sizeof(**vols); + + return 0; +} + #ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 9dd835527e02..d3b60ae7d743 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -561,6 +561,12 @@ int avs_ipc_set_system_time(struct avs_dev *adev); #define AVS_COPIER_MOD_UUID \ GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA) +#define AVS_PEAKVOL_MOD_UUID \ + GUID_INIT(0x8A171323, 0x94A3, 0x4E1D, 0xAF, 0xE9, 0xFE, 0x5D, 0xBA, 0xa4, 0xC3, 0x93) + +#define AVS_GAIN_MOD_UUID \ + GUID_INIT(0x61BCA9A8, 0x18D0, 0x4A18, 0x8E, 0x7B, 0x26, 0x39, 0x21, 0x98, 0x04, 0xB7) + #define AVS_KPBUFF_MOD_UUID \ GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6) @@ -729,6 +735,19 @@ struct avs_copier_cfg { struct avs_copier_gtw_cfg gtw_cfg; } __packed; +struct avs_volume_cfg { + u32 channel_id; + u32 target_volume; + u32 curve_type; + u32 reserved; /* alignment */ + u64 curve_duration; +} __packed; + +struct avs_peakvol_cfg { + struct avs_modcfg_base base; + struct avs_volume_cfg vols[]; +} __packed; + struct avs_micsel_cfg { struct avs_modcfg_base base; struct avs_audio_format out_fmt; @@ -802,6 +821,20 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, const struct avs_audio_format *src_fmt, const struct avs_audio_format *sink_fmt); +enum avs_peakvol_runtime_param { + AVS_PEAKVOL_VOLUME = 0, +}; + +enum avs_audio_curve_type { + AVS_AUDIO_CURVE_NONE = 0, + AVS_AUDIO_CURVE_WINDOWS_FADE = 1, +}; + +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol); +int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg **vols, size_t *num_vols); + #define AVS_PROBE_INST_ID 0 enum avs_probe_runtime_param { diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index ce157a8d6552..05302ab705ae 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -10,6 +10,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "avs.h" +#include "control.h" #include "path.h" #include "topology.h" @@ -264,6 +265,65 @@ static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) return ret; } +static struct avs_control_data *avs_get_module_control(struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_path_template *path_tmpl; + struct snd_soc_dapm_widget *w; + int i; + + path_tmpl = t->owner->owner->owner; + w = path_tmpl->w; + + for (i = 0; i < w->num_kcontrols; i++) { + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + + mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value; + ctl_data = (struct avs_control_data *)mc->dobj.private; + if (ctl_data->id == t->ctl_id) + return ctl_data; + } + + return NULL; +} + +static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_control_data *ctl_data; + struct avs_peakvol_cfg *cfg; + int volume = S32_MAX; + size_t size; + int ret; + + ctl_data = avs_get_module_control(mod); + if (ctl_data) + volume = ctl_data->volume; + + /* As 2+ channels controls are unsupported, have a single block for all channels. */ + size = struct_size(cfg, vols, 1); + cfg = kzalloc(size, GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->vols[0].target_volume = volume; + cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK; + cfg->vols[0].curve_type = AVS_AUDIO_CURVE_NONE; + cfg->vols[0].curve_duration = 0; + + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, size, &mod->instance_id); + + kfree(cfg); + return ret; +} + static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod) { struct avs_tplg_module *t = mod->template; @@ -465,6 +525,8 @@ static struct avs_module_create avs_module_create[] = { { &AVS_MIXOUT_MOD_UUID, avs_modbase_create }, { &AVS_KPBUFF_MOD_UUID, avs_modbase_create }, { &AVS_COPIER_MOD_UUID, avs_copier_create }, + { &AVS_PEAKVOL_MOD_UUID, avs_peakvol_create }, + { &AVS_GAIN_MOD_UUID, avs_peakvol_create }, { &AVS_MICSEL_MOD_UUID, avs_micsel_create }, { &AVS_MUX_MOD_UUID, avs_mux_create }, { &AVS_UPDWMIX_MOD_UUID, avs_updown_mix_create }, diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index f930c5e86a84..31c032a0f7e4 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -35,15 +35,13 @@ struct avs_dma_data { static struct avs_tplg_path_template * avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) { - struct snd_soc_dapm_widget *dw; + struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction); struct snd_soc_dapm_path *dp; enum snd_soc_dapm_direction dir; if (direction == SNDRV_PCM_STREAM_CAPTURE) { - dw = dai->capture_widget; dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN; } else { - dw = dai->playback_widget; dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT; } @@ -60,7 +58,7 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe, const struct snd_soc_dai_ops *ops) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_tplg_path_template *template; struct avs_dma_data *data; @@ -169,7 +167,7 @@ static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data; @@ -218,7 +216,7 @@ static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dma_data *data; int ret = 0; @@ -305,7 +303,7 @@ static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream, static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct avs_dma_data *data; - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct hdac_ext_stream *link_stream; struct hdac_ext_link *link; struct hda_codec *codec; @@ -335,7 +333,7 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct hdac_ext_stream *link_stream = runtime->private_data; struct hdac_ext_link *link; @@ -374,7 +372,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct hdac_ext_stream *link_stream; struct avs_dma_data *data; int ret = 0; @@ -489,7 +487,7 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dev *adev = to_avs_dev(dai->dev); struct avs_dma_data *data; @@ -628,7 +626,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dma_data *data; struct hdac_ext_stream *host_stream; struct hdac_bus *bus; @@ -647,7 +645,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_start(hdac_stream(host_stream), true); + snd_hdac_stream_start(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, flags); /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ @@ -836,7 +834,7 @@ static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data int ret; substream = data->substream; - rtd = snd_pcm_substream_chip(substream); + rtd = asoc_substream_to_rtd(substream); ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai); if (ret) @@ -929,9 +927,9 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be, int ret; for_each_component_dais(component, dai) { - data = dai->playback_dma_data; + data = snd_soc_dai_dma_data_get_playback(dai); if (data) { - rtd = snd_pcm_substream_chip(data->substream); + rtd = asoc_substream_to_rtd(data->substream); if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { ret = op(dai, data); if (ret < 0) { @@ -942,9 +940,9 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be, } } - data = dai->capture_dma_data; + data = snd_soc_dai_dma_data_get_capture(dai); if (data) { - rtd = snd_pcm_substream_chip(data->substream); + rtd = asoc_substream_to_rtd(data->substream); if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { ret = op(dai, data); if (ret < 0) { @@ -1048,7 +1046,7 @@ static const struct snd_pcm_hardware avs_pcm_hardware = { static int avs_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); /* only FE DAI links are handled here */ if (rtd->dai_link->no_pcm) @@ -1066,7 +1064,7 @@ static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) static snd_pcm_uframes_t avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct avs_dma_data *data; struct hdac_ext_stream *host_stream; unsigned int pos; @@ -1291,11 +1289,14 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); for_each_component_dais_safe(component, dai, save) { + int stream; + if (!strstr(dai->driver->name, name)) continue; - snd_soc_dapm_free_widget(dai->playback_widget); - snd_soc_dapm_free_widget(dai->capture_widget); + for_each_pcm_streams(stream) + snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); + snd_soc_unregister_dai(dai); } } @@ -1394,7 +1395,7 @@ static void avs_component_hda_remove(struct snd_soc_component *component) static int avs_component_hda_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct hdac_ext_stream *link_stream; struct hda_codec *codec; @@ -1441,7 +1442,7 @@ static int avs_component_hda_open(struct snd_soc_component *component, static int avs_component_hda_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct hdac_ext_stream *link_stream; /* only BE DAI links are handled here */ diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 29d63f2a9616..70a94201d6a5 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -190,7 +190,7 @@ static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: spin_lock_irqsave(&bus->reg_lock, cookie); - snd_hdac_stream_start(hdac_stream(host_stream), true); + snd_hdac_stream_start(hdac_stream(host_stream)); spin_unlock_irqrestore(&bus->reg_lock, cookie); break; @@ -277,31 +277,8 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = { }, }; -static int avs_probe_component_probe(struct snd_soc_component *component) -{ - struct avs_soc_component *acomp = to_avs_soc_component(component); - struct avs_dev *adev = to_avs_dev(component->dev); - - mutex_lock(&adev->comp_list_mutex); - list_add_tail(&acomp->node, &adev->comp_list); - mutex_unlock(&adev->comp_list_mutex); - return 0; -} - -static void avs_probe_component_remove(struct snd_soc_component *component) -{ - struct avs_soc_component *acomp = to_avs_soc_component(component); - struct avs_dev *adev = to_avs_dev(component->dev); - - mutex_lock(&adev->comp_list_mutex); - list_del(&acomp->node); - mutex_unlock(&adev->comp_list_mutex); -} - static const struct snd_soc_component_driver avs_probe_component_driver = { .name = "avs-probe-compr", - .probe = avs_probe_component_probe, - .remove = avs_probe_component_remove, .compress_ops = &avs_probe_compress_ops, .module_get_upon_open = 1, /* increment refcount when a stream is opened */ }; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index e845eaf0a1e7..cdb4ec500261 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -13,6 +13,7 @@ #include <sound/soc-topology.h> #include <uapi/sound/intel/avs/tokens.h> #include "avs.h" +#include "control.h" #include "topology.h" /* Get pointer to vendor array at the specified offset. */ @@ -1070,6 +1071,12 @@ static const struct avs_tplg_token_parser module_parsers[] = { .offset = offsetof(struct avs_tplg_module, cfg_ext), .parse = avs_parse_modcfg_ext_ptr, }, + { + .token = AVS_TKN_MOD_KCONTROL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, ctl_id), + .parse = avs_parse_byte_token, + }, }; static struct avs_tplg_module * @@ -1435,6 +1442,16 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, return 0; } +static int avs_widget_ready(struct snd_soc_component *comp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *dw) +{ + struct avs_tplg_path_template *template = w->priv; + + template->w = w; + return 0; +} + static int avs_dai_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) @@ -1586,9 +1603,68 @@ static int avs_manifest(struct snd_soc_component *comp, int index, return avs_tplg_parse_bindings(comp, tuples, remaining); } +#define AVS_CONTROL_OPS_VOLUME 257 + +static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = { + { + .id = AVS_CONTROL_OPS_VOLUME, + .get = avs_control_volume_get, + .put = avs_control_volume_put, + }, +}; + +static const struct avs_tplg_token_parser control_parsers[] = { + { + .token = AVS_TKN_KCONTROL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_control_data, id), + .parse = avs_parse_word_token, + }, +}; + +static int +avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_new *ctmpl, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_soc_tplg_vendor_array *tuples; + struct snd_soc_tplg_mixer_control *tmc; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + size_t block_size; + int ret; + + switch (le32_to_cpu(hdr->type)) { + case SND_SOC_TPLG_TYPE_MIXER: + tmc = container_of(hdr, typeof(*tmc), hdr); + tuples = tmc->priv.array; + block_size = le32_to_cpu(tmc->priv.size); + break; + default: + return -EINVAL; + } + + ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL); + if (!ctl_data) + return -ENOMEM; + + ret = parse_dictionary_entries(comp, tuples, block_size, ctl_data, 1, sizeof(*ctl_data), + AVS_TKN_KCONTROL_ID_U32, control_parsers, + ARRAY_SIZE(control_parsers)); + if (ret) + return ret; + + mc = (struct soc_mixer_control *)ctmpl->private_value; + mc->dobj.private = ctl_data; + return 0; +} + static struct snd_soc_tplg_ops avs_tplg_ops = { + .io_ops = avs_control_ops, + .io_ops_count = ARRAY_SIZE(avs_control_ops), + .control_load = avs_control_load, .dapm_route_load = avs_route_load, .widget_load = avs_widget_load, + .widget_ready = avs_widget_ready, .dai_load = avs_dai_load, .link_load = avs_link_load, .manifest = avs_manifest, diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 68e5f6312353..6e1c8e9b2496 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -138,6 +138,8 @@ struct avs_tplg_path_template_id { struct avs_tplg_path_template { u32 id; + struct snd_soc_dapm_widget *w; + struct list_head path_list; struct avs_tplg *owner; @@ -180,6 +182,7 @@ struct avs_tplg_module { u8 core_id; u8 domain; struct avs_tplg_modcfg_ext *cfg_ext; + u32 ctl_id; struct avs_tplg_pipeline *owner; /* Pipeline modules management. */ diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c index fcb7cfc823d6..c63eea909b5e 100644 --- a/sound/soc/intel/avs/trace.c +++ b/sound/soc/intel/avs/trace.c @@ -24,7 +24,7 @@ void trace_avs_msg_payload(const void *data, size_t size) while (remaining > 0) { u32 chunk; - chunk = min(remaining, (size_t)MAX_CHUNK_SIZE); + chunk = min_t(size_t, remaining, MAX_CHUNK_SIZE); trace_avs_ipc_msg_payload(data, chunk, offset, size); remaining -= chunk; diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 41cec67157b6..9942a2de6f7a 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -253,9 +253,9 @@ static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) if (adev) { snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - put_device(&adev->dev); byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name; } + acpi_dev_put(adev); /* override platform name, if required */ ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index a0c8f1d3f8ce..a3b0cfab17b0 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -256,9 +256,9 @@ static int bytcht_da7213_probe(struct platform_device *pdev) if (adev) { snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - put_device(&adev->dev); dailink[dai_index].codecs->name = codec_name; } + acpi_dev_put(adev); /* override platform name, if required */ platform_name = mach->mach_params.platform; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 25a054bd4073..875bc0b3d85d 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -581,9 +581,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (adev) { snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "i2c-%s", acpi_dev_name(adev)); - put_device(&adev->dev); cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name; } + acpi_dev_put(adev); /* * swap SSP0 if bytcr is detected diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 49d3da8f7316..daa630a0efc1 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -473,9 +473,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (adev) { snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", acpi_dev_name(adev)); - put_device(&adev->dev); cht_dailink[dai_index].codecs->name = drv->codec_name; } + acpi_dev_put(adev); /* Use SSP0 on Bay Trail CR devices */ if (soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0) { diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 7b74f122e340..ffd9c583dab1 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -105,7 +105,6 @@ static int sof_card_late_probe(struct snd_soc_card *card) char jack_name[NAME_SIZE]; struct sof_hdmi_pcm *pcm; int err; - int i; if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT)) return 0; @@ -124,7 +123,6 @@ static int sof_card_late_probe(struct snd_soc_card *card) return hda_dsp_hdmi_build_controls(card, component); } - i = 0; list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -139,8 +137,6 @@ static int sof_card_late_probe(struct snd_soc_card *card) &pcm->sof_hdmi); if (err < 0) return err; - - i++; } return hdac_hdmi_jack_port_init(component, &card->dapm); diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c index 54395e2ededc..fbad5a73de44 100644 --- a/sound/soc/intel/boards/sof_wm8804.c +++ b/sound/soc/intel/boards/sof_wm8804.c @@ -269,9 +269,9 @@ static int sof_wm8804_probe(struct platform_device *pdev) if (adev) { snprintf(codec_name, sizeof(codec_name), "%s%s", "i2c-", acpi_dev_name(adev)); - put_device(&adev->dev); dailink[dai_index].codecs->name = codec_name; } + acpi_dev_put(adev); snd_soc_card_set_drvdata(card, ctx); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 5ab0917a2b3d..d31509298a0a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -549,7 +549,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0) return; - memcpy(cpr_mconfig->gtw_cfg.config_data, + memcpy(&cpr_mconfig->gtw_cfg.config_data, mconfig->formats_config[SKL_PARAM_INIT].caps, mconfig->formats_config[SKL_PARAM_INIT].caps_size); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dc627d18518d..a4209d88b0c6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -449,7 +449,7 @@ static int skl_decoupled_trigger(struct snd_pcm_substream *substream, spin_lock_irqsave(&bus->reg_lock, cookie); if (start) { - snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_start(hdac_stream(stream)); snd_hdac_stream_timecounter_init(hstr, 0); } else { snd_hdac_stream_stop(hdac_stream(stream)); @@ -1134,7 +1134,7 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, continue; stream = get_hdac_ext_stream(s); if (start) - snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_start(hdac_stream(stream)); else snd_hdac_stream_stop(hdac_stream(stream)); } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b20643b83401..96cfebded072 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1663,11 +1663,10 @@ int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg * skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct snd_soc_dapm_path *p = NULL; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && !is_skl_dsp_widget_type(p->sink, dai->dev)) @@ -1680,7 +1679,6 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) } } } else { - w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && !is_skl_dsp_widget_type(p->source, dai->dev)) @@ -1744,14 +1742,12 @@ static struct skl_module_cfg *skl_get_mconfig_cap_cpr( struct skl_module_cfg * skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct skl_module_cfg *mconfig; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; mconfig = skl_get_mconfig_pb_cpr(dai, w); } else { - w = dai->capture_widget; mconfig = skl_get_mconfig_cap_cpr(dai, w); } return mconfig; @@ -1905,20 +1901,13 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, params->stream); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; - return skl_tplg_be_set_src_pipe_params(dai, w, params); - } else { - w = dai->capture_widget; - return skl_tplg_be_set_sink_pipe_params(dai, w, params); } - - return 0; } static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { @@ -2978,7 +2967,7 @@ void skl_cleanup_resources(struct skl_dev *skl) return; card = soc_component->card; - if (!card || !card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return; list_for_each_entry(w, &card->widgets, list) { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 6db0fd7bad49..30a0977af943 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -115,7 +115,10 @@ struct skl_cpr_gtw_cfg { u32 dma_buffer_size; u32 config_length; /* not mandatory; required only for DMIC/I2S */ - u32 config_data[1]; + struct { + u32 gtw_attrs; + u32 data[]; + } config_data; } __packed; struct skl_dma_control { diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 700a18561a94..640cebd2983e 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -86,7 +86,7 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win, /* try to find matching cs for current dma address */ for (i = 0; i < dram->num_cs; i++) { - const struct mbus_dram_window *cs = dram->cs + i; + const struct mbus_dram_window *cs = &dram->cs[i]; if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) { writel(cs->base & 0xffff0000, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win)); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index b027fba8233d..4baac72677d9 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -208,6 +208,29 @@ config SND_SOC_MTK_BTCVSD Select Y if you have such device. If unsure select "N". +config SND_SOC_MT8188 + tristate "ASoC support for MediaTek MT8188 chip" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on COMMON_CLK + select SND_SOC_MEDIATEK + select MFD_SYSCON if SND_SOC_MT6359 + help + This adds ASoC platform driver support for MediaTek MT8188 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8188_MT6359 + tristate "ASoC Audio driver for MT8188 with MT6359 and I2S codecs" + depends on SND_SOC_MT8188 && MTK_PMIC_WRAP + select SND_SOC_MT6359 + select SND_SOC_HDMI_CODEC + help + This adds support for ASoC machine driver for MediaTek MT8188 + boards with the MT6359 and other I2S audio codecs. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MT8192 tristate "ASoC support for Mediatek MT8192 chip" depends on ARCH_MEDIATEK diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 5571c640a288..3de38cfc69e5 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ obj-$(CONFIG_SND_SOC_MT8186) += mt8186/ +obj-$(CONFIG_SND_SOC_MT8188) += mt8188/ obj-$(CONFIG_SND_SOC_MT8192) += mt8192/ obj-$(CONFIG_SND_SOC_MT8195) += mt8195/ diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile index 576deb7f8cce..42e636c10c1e 100644 --- a/sound/soc/mediatek/common/Makefile +++ b/sound/soc/mediatek/common/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # platform driver -snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o mtk-dsp-sof-common.o +snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o mtk-dsp-sof-common.o mtk-soundcard-driver.o obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o obj-$(CONFIG_SND_SOC_MTK_BTCVSD) += mtk-btcvsd.o diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index ef83e78c22a8..f51578b6c50a 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -9,7 +9,26 @@ #ifndef _MTK_BASE_AFE_H_ #define _MTK_BASE_AFE_H_ +#include <linux/soc/mediatek/mtk_sip_svc.h> + #define MTK_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1) +#define MTK_SIP_AUDIO_CONTROL MTK_SIP_SMC_CMD(0x517) + +/* SMC CALL Operations */ +enum mtk_audio_smc_call_op { + MTK_AUDIO_SMC_OP_INIT = 0, + MTK_AUDIO_SMC_OP_DRAM_REQUEST, + MTK_AUDIO_SMC_OP_DRAM_RELEASE, + MTK_AUDIO_SMC_OP_SRAM_REQUEST, + MTK_AUDIO_SMC_OP_SRAM_RELEASE, + MTK_AUDIO_SMC_OP_ADSP_REQUEST, + MTK_AUDIO_SMC_OP_ADSP_RELEASE, + MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS, + MTK_AUDIO_SMC_OP_BTCVSD_WRITE, + MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_CLEAR, + MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_UNDERFLOW, + MTK_AUDIO_SMC_OP_NUM +}; struct mtk_base_memif_data { int id; diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c index 8b1b623207be..6fef16306f74 100644 --- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c +++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c @@ -32,7 +32,7 @@ int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, continue; for_each_rtd_cpu_dais(runtime, j, cpu_dai) { - if (cpu_dai->stream_active[conn->stream_dir] > 0) { + if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) { sof_dai_link = runtime->dai_link; break; } @@ -111,21 +111,17 @@ int mtk_sof_card_late_probe(struct snd_soc_card *card) for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { struct snd_soc_dapm_route route; struct snd_soc_dapm_path *p = NULL; - struct snd_soc_dapm_widget *play_widget = - cpu_dai->playback_widget; - struct snd_soc_dapm_widget *cap_widget = - cpu_dai->capture_widget; + struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir); + memset(&route, 0, sizeof(route)); - if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && - cap_widget) { - snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) { + snd_soc_dapm_widget_for_each_sink_path(widget, p) { route.source = conn->sof_dma; route.sink = p->sink->name; snd_soc_dapm_add_routes(&card->dapm, &route, 1); } - } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && - play_widget) { - snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) { + snd_soc_dapm_widget_for_each_source_path(widget, p) { route.source = p->source->name; route.sink = conn->sof_dma; snd_soc_dapm_add_routes(&card->dapm, &route, 1); diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c new file mode 100644 index 000000000000..7c55c2cb1f21 --- /dev/null +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtk-soundcard-driver.c -- MediaTek soundcard driver common + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Trevor Wu <trevor.wu@mediatek.com> + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <sound/soc.h> + +#include "mtk-soundcard-driver.h" + +static int set_card_codec_info(struct snd_soc_card *card, + struct device_node *sub_node, + struct snd_soc_dai_link *dai_link) +{ + struct device *dev = card->dev; + struct device_node *codec_node; + int ret; + + codec_node = of_get_child_by_name(sub_node, "codec"); + if (!codec_node) + return -EINVAL; + + /* set card codec info */ + ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); + + of_node_put(codec_node); + + if (ret < 0) + return dev_err_probe(dev, ret, "%s: codec dai not found\n", + dai_link->name); + + return 0; +} + +int parse_dai_link_info(struct snd_soc_card *card) +{ + struct device *dev = card->dev; + struct device_node *sub_node; + struct snd_soc_dai_link *dai_link; + const char *dai_link_name; + int ret, i; + + /* Loop over all the dai link sub nodes */ + for_each_available_child_of_node(dev->of_node, sub_node) { + if (of_property_read_string(sub_node, "link-name", + &dai_link_name)) + return -EINVAL; + + for_each_card_prelinks(card, i, dai_link) { + if (!strcmp(dai_link_name, dai_link->name)) + break; + } + + if (i >= card->num_links) + return -EINVAL; + + ret = set_card_codec_info(card, sub_node, dai_link); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(parse_dai_link_info); + +void clean_card_reference(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int i; + + /* release codec reference gotten by set_card_codec_info */ + for_each_card_prelinks(card, i, dai_link) + snd_soc_of_put_dai_link_codecs(dai_link); +} +EXPORT_SYMBOL_GPL(clean_card_reference); diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.h b/sound/soc/mediatek/common/mtk-soundcard-driver.h new file mode 100644 index 000000000000..d92cac1d7b72 --- /dev/null +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mtk-soundcard-driver.h -- MediaTek soundcard driver common definition + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Trevor Wu <trevor.wu@mediatek.com> + */ + +#ifndef _MTK_SOUNDCARD_DRIVER_H_ +#define _MTK_SOUNDCARD_DRIVER_H_ + +int parse_dai_link_info(struct snd_soc_card *card); +void clean_card_reference(struct snd_soc_card *card); +#endif diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c index 51f736f319e4..8a309b0734f7 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c @@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt6797_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -193,10 +195,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, + c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c index 38ce0e36cdb4..4e25287fc0e4 100644 --- a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c @@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -193,10 +195,9 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index d7e94e6a19c7..41172a82103e 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -2395,6 +2395,7 @@ static bool mt8186_is_volatile_reg(struct device *dev, unsigned int reg) case AUDIO_TOP_CON1: /* reg bit controlled by CCF */ case AUDIO_TOP_CON2: case AUDIO_TOP_CON3: + case AFE_DAC_CON0: case AFE_DL1_CUR_MSB: case AFE_DL1_CUR: case AFE_DL1_END: diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c index 41221a66111c..a50aa294960b 100644 --- a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c @@ -218,6 +218,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8186_afe_private *afe_priv = afe->platform_priv; + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); int pcm_id = dai->id; struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id]; unsigned int rate = params_rate(params); @@ -230,12 +232,11 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int pcm_con = 0; dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", - __func__, dai->id, substream->stream, dai->playback_widget->active, - dai->capture_widget->active); + __func__, dai->id, substream->stream, p->active, c->active); dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n", __func__, rate, rate_reg, data_width, wlen_width); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c index af44e331dae8..b333950aa3c3 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c @@ -140,7 +140,7 @@ static int primary_codec_init(struct snd_soc_pcm_runtime *rtd) if (!priv->dmic_sel) { dev_info(card->dev, "dmic_sel is null\n"); - return ret; + return 0; } ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, diff --git a/sound/soc/mediatek/mt8188/Makefile b/sound/soc/mediatek/mt8188/Makefile new file mode 100644 index 000000000000..781e61cbb22b --- /dev/null +++ b/sound/soc/mediatek/mt8188/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt8188-afe-objs := \ + mt8188-afe-clk.o \ + mt8188-afe-pcm.o \ + mt8188-audsys-clk.o \ + mt8188-dai-adda.o \ + mt8188-dai-etdm.o \ + mt8188-dai-pcm.o + +obj-$(CONFIG_SND_SOC_MT8188) += snd-soc-mt8188-afe.o + +# machine driver +obj-$(CONFIG_SND_SOC_MT8188_MT6359) += mt8188-mt6359.o diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c new file mode 100644 index 000000000000..743d6a162cb9 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt8188-afe-clk.c -- MediaTek 8188 afe clock ctrl + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/clk.h> + +#include "mt8188-afe-common.h" +#include "mt8188-afe-clk.h" +#include "mt8188-audsys-clk.h" +#include "mt8188-reg.h" + +static const char *aud_clks[MT8188_CLK_NUM] = { + /* xtal */ + [MT8188_CLK_XTAL_26M] = "clk26m", + + /* pll */ + [MT8188_CLK_APMIXED_APLL1] = "apll1", + [MT8188_CLK_APMIXED_APLL2] = "apll2", + + /* divider */ + [MT8188_CLK_TOP_APLL12_DIV0] = "apll12_div0", + [MT8188_CLK_TOP_APLL12_DIV1] = "apll12_div1", + [MT8188_CLK_TOP_APLL12_DIV2] = "apll12_div2", + [MT8188_CLK_TOP_APLL12_DIV3] = "apll12_div3", + [MT8188_CLK_TOP_APLL12_DIV9] = "apll12_div9", + + /* mux */ + [MT8188_CLK_TOP_A1SYS_HP_SEL] = "top_a1sys_hp", + [MT8188_CLK_TOP_AUD_INTBUS_SEL] = "top_aud_intbus", + [MT8188_CLK_TOP_AUDIO_H_SEL] = "top_audio_h", + [MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "top_audio_local_bus", + [MT8188_CLK_TOP_DPTX_M_SEL] = "top_dptx", + [MT8188_CLK_TOP_I2SO1_M_SEL] = "top_i2so1", + [MT8188_CLK_TOP_I2SO2_M_SEL] = "top_i2so2", + [MT8188_CLK_TOP_I2SI1_M_SEL] = "top_i2si1", + [MT8188_CLK_TOP_I2SI2_M_SEL] = "top_i2si2", + + /* clock gate */ + [MT8188_CLK_ADSP_AUDIO_26M] = "adsp_audio_26m", + /* afe clock gate */ + [MT8188_CLK_AUD_AFE] = "aud_afe", + [MT8188_CLK_AUD_APLL1_TUNER] = "aud_apll1_tuner", + [MT8188_CLK_AUD_APLL2_TUNER] = "aud_apll2_tuner", + [MT8188_CLK_AUD_APLL] = "aud_apll", + [MT8188_CLK_AUD_APLL2] = "aud_apll2", + [MT8188_CLK_AUD_DAC] = "aud_dac", + [MT8188_CLK_AUD_ADC] = "aud_adc", + [MT8188_CLK_AUD_DAC_HIRES] = "aud_dac_hires", + [MT8188_CLK_AUD_A1SYS_HP] = "aud_a1sys_hp", + [MT8188_CLK_AUD_ADC_HIRES] = "aud_adc_hires", + [MT8188_CLK_AUD_I2SIN] = "aud_i2sin", + [MT8188_CLK_AUD_TDM_IN] = "aud_tdm_in", + [MT8188_CLK_AUD_I2S_OUT] = "aud_i2s_out", + [MT8188_CLK_AUD_TDM_OUT] = "aud_tdm_out", + [MT8188_CLK_AUD_HDMI_OUT] = "aud_hdmi_out", + [MT8188_CLK_AUD_ASRC11] = "aud_asrc11", + [MT8188_CLK_AUD_ASRC12] = "aud_asrc12", + [MT8188_CLK_AUD_A1SYS] = "aud_a1sys", + [MT8188_CLK_AUD_A2SYS] = "aud_a2sys", + [MT8188_CLK_AUD_PCMIF] = "aud_pcmif", + [MT8188_CLK_AUD_MEMIF_UL1] = "aud_memif_ul1", + [MT8188_CLK_AUD_MEMIF_UL2] = "aud_memif_ul2", + [MT8188_CLK_AUD_MEMIF_UL3] = "aud_memif_ul3", + [MT8188_CLK_AUD_MEMIF_UL4] = "aud_memif_ul4", + [MT8188_CLK_AUD_MEMIF_UL5] = "aud_memif_ul5", + [MT8188_CLK_AUD_MEMIF_UL6] = "aud_memif_ul6", + [MT8188_CLK_AUD_MEMIF_UL8] = "aud_memif_ul8", + [MT8188_CLK_AUD_MEMIF_UL9] = "aud_memif_ul9", + [MT8188_CLK_AUD_MEMIF_UL10] = "aud_memif_ul10", + [MT8188_CLK_AUD_MEMIF_DL2] = "aud_memif_dl2", + [MT8188_CLK_AUD_MEMIF_DL3] = "aud_memif_dl3", + [MT8188_CLK_AUD_MEMIF_DL6] = "aud_memif_dl6", + [MT8188_CLK_AUD_MEMIF_DL7] = "aud_memif_dl7", + [MT8188_CLK_AUD_MEMIF_DL8] = "aud_memif_dl8", + [MT8188_CLK_AUD_MEMIF_DL10] = "aud_memif_dl10", + [MT8188_CLK_AUD_MEMIF_DL11] = "aud_memif_dl11", +}; + +struct mt8188_afe_tuner_cfg { + unsigned int id; + int apll_div_reg; + unsigned int apll_div_shift; + unsigned int apll_div_maskbit; + unsigned int apll_div_default; + int ref_ck_sel_reg; + unsigned int ref_ck_sel_shift; + unsigned int ref_ck_sel_maskbit; + unsigned int ref_ck_sel_default; + int tuner_en_reg; + unsigned int tuner_en_shift; + unsigned int tuner_en_maskbit; + int upper_bound_reg; + unsigned int upper_bound_shift; + unsigned int upper_bound_maskbit; + unsigned int upper_bound_default; + spinlock_t ctrl_lock; /* lock for apll tuner ctrl*/ + int ref_cnt; +}; + +static struct mt8188_afe_tuner_cfg + mt8188_afe_tuner_cfgs[MT8188_AUD_PLL_NUM] = { + [MT8188_AUD_PLL1] = { + .id = MT8188_AUD_PLL1, + .apll_div_reg = AFE_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0xf, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_APLL_TUNER_CFG, + .ref_ck_sel_shift = 1, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x2, + .tuner_en_reg = AFE_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_APLL_TUNER_CFG, + .upper_bound_shift = 8, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x3, + }, + [MT8188_AUD_PLL2] = { + .id = MT8188_AUD_PLL2, + .apll_div_reg = AFE_APLL_TUNER_CFG1, + .apll_div_shift = 4, + .apll_div_maskbit = 0xf, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_APLL_TUNER_CFG1, + .ref_ck_sel_shift = 1, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x1, + .tuner_en_reg = AFE_APLL_TUNER_CFG1, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_APLL_TUNER_CFG1, + .upper_bound_shift = 8, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x3, + }, + [MT8188_AUD_PLL3] = { + .id = MT8188_AUD_PLL3, + .apll_div_reg = AFE_EARC_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x3, + .ref_ck_sel_reg = AFE_EARC_APLL_TUNER_CFG, + .ref_ck_sel_shift = 24, + .ref_ck_sel_maskbit = 0x3, + .ref_ck_sel_default = 0x0, + .tuner_en_reg = AFE_EARC_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_EARC_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, + [MT8188_AUD_PLL4] = { + .id = MT8188_AUD_PLL4, + .apll_div_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x7, + .ref_ck_sel_reg = AFE_SPDIFIN_APLL_TUNER_CFG1, + .ref_ck_sel_shift = 8, + .ref_ck_sel_maskbit = 0x1, + .ref_ck_sel_default = 0, + .tuner_en_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_SPDIFIN_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, + [MT8188_AUD_PLL5] = { + .id = MT8188_AUD_PLL5, + .apll_div_reg = AFE_LINEIN_APLL_TUNER_CFG, + .apll_div_shift = 4, + .apll_div_maskbit = 0x3f, + .apll_div_default = 0x3, + .ref_ck_sel_reg = AFE_LINEIN_APLL_TUNER_CFG, + .ref_ck_sel_shift = 24, + .ref_ck_sel_maskbit = 0x1, + .ref_ck_sel_default = 0, + .tuner_en_reg = AFE_LINEIN_APLL_TUNER_CFG, + .tuner_en_shift = 0, + .tuner_en_maskbit = 0x1, + .upper_bound_reg = AFE_LINEIN_APLL_TUNER_CFG, + .upper_bound_shift = 12, + .upper_bound_maskbit = 0xff, + .upper_bound_default = 0x4, + }, +}; + +static struct mt8188_afe_tuner_cfg *mt8188_afe_found_apll_tuner(unsigned int id) +{ + if (id >= MT8188_AUD_PLL_NUM) + return NULL; + + return &mt8188_afe_tuner_cfgs[id]; +} + +static int mt8188_afe_init_apll_tuner(unsigned int id) +{ + struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id); + + if (!cfg) + return -EINVAL; + + cfg->ref_cnt = 0; + spin_lock_init(&cfg->ctrl_lock); + + return 0; +} + +static int mt8188_afe_setup_apll_tuner(struct mtk_base_afe *afe, unsigned int id) +{ + const struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id); + + if (!cfg) + return -EINVAL; + + regmap_update_bits(afe->regmap, + cfg->apll_div_reg, + cfg->apll_div_maskbit << cfg->apll_div_shift, + cfg->apll_div_default << cfg->apll_div_shift); + + regmap_update_bits(afe->regmap, + cfg->ref_ck_sel_reg, + cfg->ref_ck_sel_maskbit << cfg->ref_ck_sel_shift, + cfg->ref_ck_sel_default << cfg->ref_ck_sel_shift); + + regmap_update_bits(afe->regmap, + cfg->upper_bound_reg, + cfg->upper_bound_maskbit << cfg->upper_bound_shift, + cfg->upper_bound_default << cfg->upper_bound_shift); + + return 0; +} + +static int mt8188_afe_enable_tuner_clk(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + switch (id) { + case MT8188_AUD_PLL1: + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]); + break; + case MT8188_AUD_PLL2: + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mt8188_afe_disable_tuner_clk(struct mtk_base_afe *afe, + unsigned int id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + switch (id) { + case MT8188_AUD_PLL1: + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]); + break; + case MT8188_AUD_PLL2: + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mt8188_afe_enable_apll_tuner(struct mtk_base_afe *afe, unsigned int id) +{ + struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id); + unsigned long flags; + int ret; + + if (!cfg) + return -EINVAL; + + ret = mt8188_afe_setup_apll_tuner(afe, id); + if (ret) + return ret; + + ret = mt8188_afe_enable_tuner_clk(afe, id); + if (ret) + return ret; + + spin_lock_irqsave(&cfg->ctrl_lock, flags); + + cfg->ref_cnt++; + if (cfg->ref_cnt == 1) + regmap_update_bits(afe->regmap, + cfg->tuner_en_reg, + cfg->tuner_en_maskbit << cfg->tuner_en_shift, + BIT(cfg->tuner_en_shift)); + + spin_unlock_irqrestore(&cfg->ctrl_lock, flags); + + return 0; +} + +static int mt8188_afe_disable_apll_tuner(struct mtk_base_afe *afe, unsigned int id) +{ + struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id); + unsigned long flags; + int ret; + + if (!cfg) + return -EINVAL; + + spin_lock_irqsave(&cfg->ctrl_lock, flags); + + cfg->ref_cnt--; + if (cfg->ref_cnt == 0) + regmap_update_bits(afe->regmap, + cfg->tuner_en_reg, + cfg->tuner_en_maskbit << cfg->tuner_en_shift, + 0 << cfg->tuner_en_shift); + else if (cfg->ref_cnt < 0) + cfg->ref_cnt = 0; + + spin_unlock_irqrestore(&cfg->ctrl_lock, flags); + + ret = mt8188_afe_disable_tuner_clk(afe, id); + if (ret) + return ret; + + return 0; +} + +int mt8188_afe_get_mclk_source_clk_id(int sel) +{ + switch (sel) { + case MT8188_MCK_SEL_26M: + return MT8188_CLK_XTAL_26M; + case MT8188_MCK_SEL_APLL1: + return MT8188_CLK_APMIXED_APLL1; + case MT8188_MCK_SEL_APLL2: + return MT8188_CLK_APMIXED_APLL2; + default: + return -EINVAL; + } +} + +int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int clk_id = mt8188_afe_get_mclk_source_clk_id(apll); + + if (clk_id < 0) { + dev_dbg(afe->dev, "invalid clk id\n"); + return 0; + } + + return clk_get_rate(afe_priv->clk[clk_id]); +} + +int mt8188_afe_get_default_mclk_source_by_rate(int rate) +{ + return ((rate % 8000) == 0) ? + MT8188_MCK_SEL_APLL1 : MT8188_MCK_SEL_APLL2; +} + +int mt8188_afe_init_clock(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int i, ret; + + ret = mt8188_audsys_clk_register(afe); + if (ret) { + dev_err(afe->dev, "register audsys clk fail %d\n", ret); + return ret; + } + + afe_priv->clk = + devm_kcalloc(afe->dev, MT8188_CLK_NUM, sizeof(*afe_priv->clk), + GFP_KERNEL); + if (!afe_priv->clk) + return -ENOMEM; + + for (i = 0; i < MT8188_CLK_NUM; i++) { + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe_priv->clk[i])) { + dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n", + __func__, aud_clks[i], + PTR_ERR(afe_priv->clk[i])); + return PTR_ERR(afe_priv->clk[i]); + } + } + + /* initial tuner */ + for (i = 0; i < MT8188_AUD_PLL_NUM; i++) { + ret = mt8188_afe_init_apll_tuner(i); + if (ret) { + dev_info(afe->dev, "%s(), init apll_tuner%d failed", + __func__, (i + 1)); + return -EINVAL; + } + } + + return 0; +} + +void mt8188_afe_deinit_clock(void *priv) +{ + struct mtk_base_afe *afe = priv; + + mt8188_audsys_clk_unregister(afe); +} + +int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk) +{ + int ret; + + if (clk) { + ret = clk_prepare_enable(clk); + if (ret) { + dev_dbg(afe->dev, "%s(), failed to enable clk\n", + __func__); + return ret; + } + } else { + dev_dbg(afe->dev, "NULL clk\n"); + } + return 0; +} +EXPORT_SYMBOL_GPL(mt8188_afe_enable_clk); + +void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk) +{ + if (clk) + clk_disable_unprepare(clk); + else + dev_dbg(afe->dev, "NULL clk\n"); +} +EXPORT_SYMBOL_GPL(mt8188_afe_disable_clk); + +int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk, + unsigned int rate) +{ + int ret; + + if (clk) { + ret = clk_set_rate(clk, rate); + if (ret) { + dev_dbg(afe->dev, "%s(), failed to set clk rate\n", + __func__); + return ret; + } + } + + return 0; +} + +int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk, + struct clk *parent) +{ + int ret; + + if (clk && parent) { + ret = clk_set_parent(clk, parent); + if (ret) { + dev_dbg(afe->dev, "%s(), failed to set clk parent\n", + __func__); + return ret; + } + } + + return 0; +} + +static unsigned int get_top_cg_reg(unsigned int cg_type) +{ + switch (cg_type) { + case MT8188_TOP_CG_A1SYS_TIMING: + case MT8188_TOP_CG_A2SYS_TIMING: + case MT8188_TOP_CG_26M_TIMING: + return ASYS_TOP_CON; + default: + return 0; + } +} + +static unsigned int get_top_cg_mask(unsigned int cg_type) +{ + switch (cg_type) { + case MT8188_TOP_CG_A1SYS_TIMING: + return ASYS_TOP_CON_A1SYS_TIMING_ON; + case MT8188_TOP_CG_A2SYS_TIMING: + return ASYS_TOP_CON_A2SYS_TIMING_ON; + case MT8188_TOP_CG_26M_TIMING: + return ASYS_TOP_CON_26M_TIMING_ON; + default: + return 0; + } +} + +static unsigned int get_top_cg_on_val(unsigned int cg_type) +{ + switch (cg_type) { + case MT8188_TOP_CG_A1SYS_TIMING: + case MT8188_TOP_CG_A2SYS_TIMING: + case MT8188_TOP_CG_26M_TIMING: + return get_top_cg_mask(cg_type); + default: + return 0; + } +} + +static unsigned int get_top_cg_off_val(unsigned int cg_type) +{ + switch (cg_type) { + case MT8188_TOP_CG_A1SYS_TIMING: + case MT8188_TOP_CG_A2SYS_TIMING: + case MT8188_TOP_CG_26M_TIMING: + return 0; + default: + return get_top_cg_mask(cg_type); + } +} + +static int mt8188_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type) +{ + unsigned int reg = get_top_cg_reg(cg_type); + unsigned int mask = get_top_cg_mask(cg_type); + unsigned int val = get_top_cg_on_val(cg_type); + + regmap_update_bits(afe->regmap, reg, mask, val); + + return 0; +} + +static int mt8188_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type) +{ + unsigned int reg = get_top_cg_reg(cg_type); + unsigned int mask = get_top_cg_mask(cg_type); + unsigned int val = get_top_cg_off_val(cg_type); + + regmap_update_bits(afe->regmap, reg, mask, val); + + return 0; +} + +int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + /* bus clock for AFE external access, like DRAM */ + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]); + + /* bus clock for AFE internal access, like AFE SRAM */ + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]); + + /* audio 26m clock source */ + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]); + + /* AFE hw clock */ + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); + + return 0; +} + +int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]); + + return 0; +} + +static int mt8188_afe_enable_afe_on(struct mtk_base_afe *afe) +{ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); + return 0; +} + +static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe) +{ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0); + return 0; +} + +static int mt8188_afe_enable_timing_sys(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); + + mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); + mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); + mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); + + return 0; +} + +static int mt8188_afe_disable_timing_sys(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]); + + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING); + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING); + mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING); + + return 0; +} + +int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe) +{ + mt8188_afe_enable_timing_sys(afe); + + mt8188_afe_enable_afe_on(afe); + + mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1); + mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2); + + return 0; +} + +int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe) +{ + mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2); + mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1); + + mt8188_afe_disable_afe_on(afe); + + mt8188_afe_disable_timing_sys(afe); + + return 0; +} diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h new file mode 100644 index 000000000000..084fdfb1d877 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8188-afe-clk.h -- MediaTek 8188 afe clock ctrl definition + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#ifndef _MT8188_AFE_CLK_H_ +#define _MT8188_AFE_CLK_H_ + +enum { + /* xtal */ + MT8188_CLK_XTAL_26M, + /* pll */ + MT8188_CLK_APMIXED_APLL1, + MT8188_CLK_APMIXED_APLL2, + /* divider */ + MT8188_CLK_TOP_APLL12_DIV0, + MT8188_CLK_TOP_APLL12_DIV1, + MT8188_CLK_TOP_APLL12_DIV2, + MT8188_CLK_TOP_APLL12_DIV3, + MT8188_CLK_TOP_APLL12_DIV9, + /* mux */ + MT8188_CLK_TOP_A1SYS_HP_SEL, + MT8188_CLK_TOP_AUD_INTBUS_SEL, + MT8188_CLK_TOP_AUDIO_H_SEL, + MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL, + MT8188_CLK_TOP_DPTX_M_SEL, + MT8188_CLK_TOP_I2SO1_M_SEL, + MT8188_CLK_TOP_I2SO2_M_SEL, + MT8188_CLK_TOP_I2SI1_M_SEL, + MT8188_CLK_TOP_I2SI2_M_SEL, + /* clock gate */ + MT8188_CLK_ADSP_AUDIO_26M, + MT8188_CLK_AUD_AFE, + MT8188_CLK_AUD_APLL1_TUNER, + MT8188_CLK_AUD_APLL2_TUNER, + MT8188_CLK_AUD_TOP0_SPDF, + MT8188_CLK_AUD_APLL, + MT8188_CLK_AUD_APLL2, + MT8188_CLK_AUD_DAC, + MT8188_CLK_AUD_ADC, + MT8188_CLK_AUD_DAC_HIRES, + MT8188_CLK_AUD_A1SYS_HP, + MT8188_CLK_AUD_ADC_HIRES, + MT8188_CLK_AUD_I2SIN, + MT8188_CLK_AUD_TDM_IN, + MT8188_CLK_AUD_I2S_OUT, + MT8188_CLK_AUD_TDM_OUT, + MT8188_CLK_AUD_HDMI_OUT, + MT8188_CLK_AUD_ASRC11, + MT8188_CLK_AUD_ASRC12, + MT8188_CLK_AUD_A1SYS, + MT8188_CLK_AUD_A2SYS, + MT8188_CLK_AUD_PCMIF, + MT8188_CLK_AUD_MEMIF_UL1, + MT8188_CLK_AUD_MEMIF_UL2, + MT8188_CLK_AUD_MEMIF_UL3, + MT8188_CLK_AUD_MEMIF_UL4, + MT8188_CLK_AUD_MEMIF_UL5, + MT8188_CLK_AUD_MEMIF_UL6, + MT8188_CLK_AUD_MEMIF_UL8, + MT8188_CLK_AUD_MEMIF_UL9, + MT8188_CLK_AUD_MEMIF_UL10, + MT8188_CLK_AUD_MEMIF_DL2, + MT8188_CLK_AUD_MEMIF_DL3, + MT8188_CLK_AUD_MEMIF_DL6, + MT8188_CLK_AUD_MEMIF_DL7, + MT8188_CLK_AUD_MEMIF_DL8, + MT8188_CLK_AUD_MEMIF_DL10, + MT8188_CLK_AUD_MEMIF_DL11, + MT8188_CLK_NUM, +}; + +enum { + MT8188_AUD_PLL1, + MT8188_AUD_PLL2, + MT8188_AUD_PLL3, + MT8188_AUD_PLL4, + MT8188_AUD_PLL5, + MT8188_AUD_PLL_NUM, +}; + +enum { + MT8188_MCK_SEL_26M, + MT8188_MCK_SEL_APLL1, + MT8188_MCK_SEL_APLL2, + MT8188_MCK_SEL_APLL3, + MT8188_MCK_SEL_APLL4, + MT8188_MCK_SEL_APLL5, + MT8188_MCK_SEL_NUM, +}; + +struct mtk_base_afe; + +int mt8188_afe_get_mclk_source_clk_id(int sel); +int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll); +int mt8188_afe_get_default_mclk_source_by_rate(int rate); +int mt8188_afe_init_clock(struct mtk_base_afe *afe); +void mt8188_afe_deinit_clock(void *priv); +int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk); +void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk); +int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk, + unsigned int rate); +int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk, + struct clk *parent); +int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe); +int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe); +int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe); +int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe); + +#endif diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h new file mode 100644 index 000000000000..eb7e57c239bd --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8188-afe-common.h -- MediaTek 8188 audio driver definitions + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#ifndef _MT_8188_AFE_COMMON_H_ +#define _MT_8188_AFE_COMMON_H_ + +#include <linux/list.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "../common/mtk-base-afe.h" + +enum { + MT8188_DAI_START, + MT8188_AFE_MEMIF_START = MT8188_DAI_START, + MT8188_AFE_MEMIF_DL2 = MT8188_AFE_MEMIF_START, + MT8188_AFE_MEMIF_DL3, + MT8188_AFE_MEMIF_DL6, + MT8188_AFE_MEMIF_DL7, + MT8188_AFE_MEMIF_DL8, + MT8188_AFE_MEMIF_DL10, + MT8188_AFE_MEMIF_DL11, + MT8188_AFE_MEMIF_UL_START, + MT8188_AFE_MEMIF_UL1 = MT8188_AFE_MEMIF_UL_START, + MT8188_AFE_MEMIF_UL2, + MT8188_AFE_MEMIF_UL3, + MT8188_AFE_MEMIF_UL4, + MT8188_AFE_MEMIF_UL5, + MT8188_AFE_MEMIF_UL6, + MT8188_AFE_MEMIF_UL8, + MT8188_AFE_MEMIF_UL9, + MT8188_AFE_MEMIF_UL10, + MT8188_AFE_MEMIF_END, + MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START), + MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END, + MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START, + MT8188_AFE_IO_DMIC_IN, + MT8188_AFE_IO_DPTX, + MT8188_AFE_IO_ETDM_START, + MT8188_AFE_IO_ETDM1_IN = MT8188_AFE_IO_ETDM_START, + MT8188_AFE_IO_ETDM2_IN, + MT8188_AFE_IO_ETDM1_OUT, + MT8188_AFE_IO_ETDM2_OUT, + MT8188_AFE_IO_ETDM3_OUT, + MT8188_AFE_IO_ETDM_END, + MT8188_AFE_IO_ETDM_NUM = + (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START), + MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END, + MT8188_AFE_IO_END, + MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START), + MT8188_DAI_END = MT8188_AFE_IO_END, + MT8188_DAI_NUM = (MT8188_DAI_END - MT8188_DAI_START), +}; + +enum { + MT8188_TOP_CG_A1SYS_TIMING, + MT8188_TOP_CG_A2SYS_TIMING, + MT8188_TOP_CG_26M_TIMING, + MT8188_TOP_CG_NUM, +}; + +enum { + MT8188_AFE_IRQ_1, + MT8188_AFE_IRQ_2, + MT8188_AFE_IRQ_3, + MT8188_AFE_IRQ_8, + MT8188_AFE_IRQ_9, + MT8188_AFE_IRQ_10, + MT8188_AFE_IRQ_13, + MT8188_AFE_IRQ_14, + MT8188_AFE_IRQ_15, + MT8188_AFE_IRQ_16, + MT8188_AFE_IRQ_17, + MT8188_AFE_IRQ_18, + MT8188_AFE_IRQ_19, + MT8188_AFE_IRQ_20, + MT8188_AFE_IRQ_21, + MT8188_AFE_IRQ_22, + MT8188_AFE_IRQ_23, + MT8188_AFE_IRQ_24, + MT8188_AFE_IRQ_25, + MT8188_AFE_IRQ_26, + MT8188_AFE_IRQ_27, + MT8188_AFE_IRQ_28, + MT8188_AFE_IRQ_NUM, +}; + +enum { + MT8188_ETDM_OUT1_1X_EN = 9, + MT8188_ETDM_OUT2_1X_EN = 10, + MT8188_ETDM_OUT3_1X_EN = 11, + MT8188_ETDM_IN1_1X_EN = 12, + MT8188_ETDM_IN2_1X_EN = 13, + MT8188_ETDM_IN1_NX_EN = 25, + MT8188_ETDM_IN2_NX_EN = 26, +}; + +enum { + MT8188_MTKAIF_MISO_0, + MT8188_MTKAIF_MISO_1, + MT8188_MTKAIF_MISO_NUM, +}; + +struct mtk_dai_memif_irq_priv { + unsigned int asys_timing_sel; +}; + +struct mtkaif_param { + bool mtkaif_calibration_ok; + int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM]; + int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM]; + int mtkaif_dmic_on; +}; + +struct clk; + +struct mt8188_afe_private { + struct clk **clk; + struct clk_lookup **lookup; + struct regmap *topckgen; + int pm_runtime_bypass_reg_ctl; + spinlock_t afe_ctrl_lock; /* Lock for afe control */ + struct mtk_dai_memif_irq_priv irq_priv[MT8188_AFE_IRQ_NUM]; + struct mtkaif_param mtkaif_params; + + /* dai */ + void *dai_priv[MT8188_DAI_NUM]; +}; + +int mt8188_afe_fs_timing(unsigned int rate); +/* dai register */ +int mt8188_dai_adda_register(struct mtk_base_afe *afe); +int mt8188_dai_etdm_register(struct mtk_base_afe *afe); +int mt8188_dai_pcm_register(struct mtk_base_afe *afe); + +#define MT8188_SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put, id) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .device = id, \ + .private_value = (unsigned long)&(xenum), \ +} + +#endif diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c new file mode 100644 index 000000000000..e8e84de86542 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c @@ -0,0 +1,3358 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC AFE platform driver for 8188 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/arm-smccc.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <sound/pcm_params.h> +#include "mt8188-afe-common.h" +#include "mt8188-afe-clk.h" +#include "mt8188-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +#define MT8188_MEMIF_BUFFER_BYTES_ALIGN (0x40) +#define MT8188_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff) + +#define MEMIF_AXI_MINLEN 9 /* register default value */ + +struct mtk_dai_memif_priv { + unsigned int asys_timing_sel; + unsigned int fs_timing; +}; + +static const struct snd_pcm_hardware mt8188_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 256 * 2 * 1024, +}; + +struct mt8188_afe_rate { + unsigned int rate; + unsigned int reg_value; +}; + +static const struct mt8188_afe_rate mt8188_afe_rates[] = { + { .rate = 8000, .reg_value = 0, }, + { .rate = 12000, .reg_value = 1, }, + { .rate = 16000, .reg_value = 2, }, + { .rate = 24000, .reg_value = 3, }, + { .rate = 32000, .reg_value = 4, }, + { .rate = 48000, .reg_value = 5, }, + { .rate = 96000, .reg_value = 6, }, + { .rate = 192000, .reg_value = 7, }, + { .rate = 384000, .reg_value = 8, }, + { .rate = 7350, .reg_value = 16, }, + { .rate = 11025, .reg_value = 17, }, + { .rate = 14700, .reg_value = 18, }, + { .rate = 22050, .reg_value = 19, }, + { .rate = 29400, .reg_value = 20, }, + { .rate = 44100, .reg_value = 21, }, + { .rate = 88200, .reg_value = 22, }, + { .rate = 176400, .reg_value = 23, }, + { .rate = 352800, .reg_value = 24, }, +}; + +int mt8188_afe_fs_timing(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8188_afe_rates); i++) + if (mt8188_afe_rates[i].rate == rate) + return mt8188_afe_rates[i].reg_value; + + return -EINVAL; +} + +static int mt8188_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = NULL; + struct mtk_base_afe *afe = NULL; + struct mt8188_afe_private *afe_priv = NULL; + struct mtk_base_afe_memif *memif = NULL; + struct mtk_dai_memif_priv *memif_priv = NULL; + int fs = mt8188_afe_fs_timing(rate); + int id = asoc_rtd_to_cpu(rtd, 0)->id; + + if (id < 0) + return -EINVAL; + + component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + if (!component) + return -EINVAL; + + afe = snd_soc_component_get_drvdata(component); + memif = &afe->memif[id]; + + switch (memif->data->id) { + case MT8188_AFE_MEMIF_DL10: + fs = MT8188_ETDM_OUT3_1X_EN; + break; + case MT8188_AFE_MEMIF_UL8: + fs = MT8188_ETDM_IN1_NX_EN; + break; + case MT8188_AFE_MEMIF_UL3: + fs = MT8188_ETDM_IN2_NX_EN; + break; + default: + afe_priv = afe->platform_priv; + memif_priv = afe_priv->dai_priv[id]; + if (memif_priv->fs_timing) + fs = memif_priv->fs_timing; + break; + } + + return fs; +} + +static int mt8188_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + int fs = mt8188_memif_fs(substream, rate); + + switch (fs) { + case MT8188_ETDM_IN1_NX_EN: + fs = MT8188_ETDM_IN1_1X_EN; + break; + case MT8188_ETDM_IN2_NX_EN: + fs = MT8188_ETDM_IN2_1X_EN; + break; + default: + break; + } + + return fs; +} + +enum { + MT8188_AFE_CM0, + MT8188_AFE_CM1, + MT8188_AFE_CM2, + MT8188_AFE_CM_NUM, +}; + +struct mt8188_afe_channel_merge { + int id; + int reg; + unsigned int sel_shift; + unsigned int sel_maskbit; + unsigned int sel_default; + unsigned int ch_num_shift; + unsigned int ch_num_maskbit; + unsigned int en_shift; + unsigned int en_maskbit; + unsigned int update_cnt_shift; + unsigned int update_cnt_maskbit; + unsigned int update_cnt_default; +}; + +static const struct mt8188_afe_channel_merge + mt8188_afe_cm[MT8188_AFE_CM_NUM] = { + [MT8188_AFE_CM0] = { + .id = MT8188_AFE_CM0, + .reg = AFE_CM0_CON, + .sel_shift = 30, + .sel_maskbit = 0x1, + .sel_default = 1, + .ch_num_shift = 2, + .ch_num_maskbit = 0x3f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x1fff, + .update_cnt_default = 0x3, + }, + [MT8188_AFE_CM1] = { + .id = MT8188_AFE_CM1, + .reg = AFE_CM1_CON, + .sel_shift = 30, + .sel_maskbit = 0x1, + .sel_default = 1, + .ch_num_shift = 2, + .ch_num_maskbit = 0x1f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x1fff, + .update_cnt_default = 0x3, + }, + [MT8188_AFE_CM2] = { + .id = MT8188_AFE_CM2, + .reg = AFE_CM2_CON, + .sel_shift = 30, + .sel_maskbit = 0x1, + .sel_default = 1, + .ch_num_shift = 2, + .ch_num_maskbit = 0x1f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x1fff, + .update_cnt_default = 0x3, + }, +}; + +static int mt8188_afe_memif_is_ul(int id) +{ + if (id >= MT8188_AFE_MEMIF_UL_START && id < MT8188_AFE_MEMIF_END) + return 1; + else + return 0; +} + +static const struct mt8188_afe_channel_merge * + mt8188_afe_found_cm(struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + int id = -EINVAL; + + if (mt8188_afe_memif_is_ul(dai->id) == 0) + return NULL; + + switch (dai->id) { + case MT8188_AFE_MEMIF_UL9: + id = MT8188_AFE_CM0; + break; + case MT8188_AFE_MEMIF_UL2: + id = MT8188_AFE_CM1; + break; + case MT8188_AFE_MEMIF_UL10: + id = MT8188_AFE_CM2; + break; + default: + break; + } + + if (id < 0) { + dev_dbg(afe->dev, "%s, memif %d cannot find CM!\n", __func__, dai->id); + return NULL; + } + + return &mt8188_afe_cm[id]; +} + +static int mt8188_afe_config_cm(struct mtk_base_afe *afe, + const struct mt8188_afe_channel_merge *cm, + unsigned int channels) +{ + if (!cm) + return -EINVAL; + + regmap_update_bits(afe->regmap, + cm->reg, + cm->sel_maskbit << cm->sel_shift, + cm->sel_default << cm->sel_shift); + + regmap_update_bits(afe->regmap, + cm->reg, + cm->ch_num_maskbit << cm->ch_num_shift, + (channels - 1) << cm->ch_num_shift); + + regmap_update_bits(afe->regmap, + cm->reg, + cm->update_cnt_maskbit << cm->update_cnt_shift, + cm->update_cnt_default << cm->update_cnt_shift); + + return 0; +} + +static int mt8188_afe_enable_cm(struct mtk_base_afe *afe, + const struct mt8188_afe_channel_merge *cm, + bool enable) +{ + if (!cm) + return -EINVAL; + + regmap_update_bits(afe->regmap, + cm->reg, + cm->en_maskbit << cm->en_shift, + enable << cm->en_shift); + + return 0; +} + +static int mt8188_afe_fe_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + int id = asoc_rtd_to_cpu(rtd, 0)->id; + int ret; + + ret = mtk_afe_fe_startup(substream, dai); + + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + MT8188_MEMIF_BUFFER_BYTES_ALIGN); + + if (id != MT8188_AFE_MEMIF_DL7) + goto out; + + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 1, + MT8188_MEMIF_DL7_MAX_PERIOD_SIZE); + if (ret < 0) + dev_dbg(afe->dev, "hw_constraint_minmax failed\n"); +out: + return ret; +} + +static void mt8188_afe_fe_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + mtk_afe_fe_shutdown(substream, dai); +} + +static int mt8188_afe_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + int id = asoc_rtd_to_cpu(rtd, 0)->id; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + const struct mtk_base_memif_data *data = memif->data; + const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai); + unsigned int channels = params_channels(params); + + mt8188_afe_config_cm(afe, cm, channels); + + if (data->ch_num_reg >= 0) { + regmap_update_bits(afe->regmap, data->ch_num_reg, + data->ch_num_maskbit << data->ch_num_shift, + channels << data->ch_num_shift); + } + + return mtk_afe_fe_hw_params(substream, params, dai); +} + +static int mt8188_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + int id = asoc_rtd_to_cpu(rtd, 0)->id; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; + const struct mtk_base_irq_data *irq_data = irqs->irq_data; + unsigned int counter = runtime->period_size; + int fs; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + mt8188_afe_enable_cm(afe, cm, true); + + ret = mtk_memif_set_enable(afe, id); + if (ret) { + dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + return ret; + } + + /* set irq counter */ + regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg, + irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift, + counter << irq_data->irq_cnt_shift); + + /* set irq fs */ + fs = afe->irq_fs(substream, runtime->rate); + + if (fs < 0) + return -EINVAL; + + if (irq_data->irq_fs_reg >= 0) + regmap_update_bits(afe->regmap, irq_data->irq_fs_reg, + irq_data->irq_fs_maskbit << irq_data->irq_fs_shift, + fs << irq_data->irq_fs_shift); + + /* delay for uplink */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + u32 sample_delay; + + sample_delay = ((MEMIF_AXI_MINLEN + 1) * 64 + + (runtime->channels * runtime->sample_bits - 1)) / + (runtime->channels * runtime->sample_bits) + 1; + + udelay(sample_delay * 1000000 / runtime->rate); + } + + /* enable interrupt */ + regmap_set_bits(afe->regmap, irq_data->irq_en_reg, + BIT(irq_data->irq_en_shift)); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + mt8188_afe_enable_cm(afe, cm, false); + + ret = mtk_memif_set_disable(afe, id); + if (ret) + dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + + /* disable interrupt */ + + regmap_clear_bits(afe->regmap, irq_data->irq_en_reg, + BIT(irq_data->irq_en_shift)); + /* and clear pending IRQ */ + regmap_write(afe->regmap, irq_data->irq_clr_reg, + BIT(irq_data->irq_clr_shift)); + return ret; + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops mt8188_afe_fe_dai_ops = { + .startup = mt8188_afe_fe_startup, + .shutdown = mt8188_afe_fe_shutdown, + .hw_params = mt8188_afe_fe_hw_params, + .hw_free = mtk_afe_fe_hw_free, + .prepare = mtk_afe_fe_prepare, + .trigger = mt8188_afe_fe_trigger, +}; + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_352800 |\ + SNDRV_PCM_RATE_384000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mt8188_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL2", + .id = MT8188_AFE_MEMIF_DL2, + .playback = { + .stream_name = "DL2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL3", + .id = MT8188_AFE_MEMIF_DL3, + .playback = { + .stream_name = "DL3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL6", + .id = MT8188_AFE_MEMIF_DL6, + .playback = { + .stream_name = "DL6", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL7", + .id = MT8188_AFE_MEMIF_DL7, + .playback = { + .stream_name = "DL7", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL8", + .id = MT8188_AFE_MEMIF_DL8, + .playback = { + .stream_name = "DL8", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL10", + .id = MT8188_AFE_MEMIF_DL10, + .playback = { + .stream_name = "DL10", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "DL11", + .id = MT8188_AFE_MEMIF_DL11, + .playback = { + .stream_name = "DL11", + .channels_min = 1, + .channels_max = 32, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL1", + .id = MT8188_AFE_MEMIF_UL1, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL2", + .id = MT8188_AFE_MEMIF_UL2, + .capture = { + .stream_name = "UL2", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL3", + .id = MT8188_AFE_MEMIF_UL3, + .capture = { + .stream_name = "UL3", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL4", + .id = MT8188_AFE_MEMIF_UL4, + .capture = { + .stream_name = "UL4", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL5", + .id = MT8188_AFE_MEMIF_UL5, + .capture = { + .stream_name = "UL5", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL6", + .id = MT8188_AFE_MEMIF_UL6, + .capture = { + .stream_name = "UL6", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL8", + .id = MT8188_AFE_MEMIF_UL8, + .capture = { + .stream_name = "UL8", + .channels_min = 1, + .channels_max = 24, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL9", + .id = MT8188_AFE_MEMIF_UL9, + .capture = { + .stream_name = "UL9", + .channels_min = 1, + .channels_max = 32, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, + { + .name = "UL10", + .id = MT8188_AFE_MEMIF_UL10, + .capture = { + .stream_name = "UL10", + .channels_min = 1, + .channels_max = 4, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8188_afe_fe_dai_ops, + }, +}; + +static const struct snd_kcontrol_new o002_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN2, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN2, 12, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN2, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN2, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN2_2, 6, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN2_2, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN2_5, 8, 1, 0), +}; + +static const struct snd_kcontrol_new o003_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN3, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN3, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN3, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN3, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN3_2, 7, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN3_2, 9, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN3_5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new o004_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN4, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN4, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN4, 24, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN4_2, 10, 1, 0), +}; + +static const struct snd_kcontrol_new o005_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN5, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN5, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN5, 25, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN5_2, 11, 1, 0), +}; + +static const struct snd_kcontrol_new o006_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN6, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN6, 16, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN6, 26, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN6_2, 12, 1, 0), +}; + +static const struct snd_kcontrol_new o007_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN7, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN7, 17, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN7, 27, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN7_2, 13, 1, 0), +}; + +static const struct snd_kcontrol_new o008_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN8, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN8, 28, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN8_2, 14, 1, 0), +}; + +static const struct snd_kcontrol_new o009_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN9, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN9, 29, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN9_2, 15, 1, 0), +}; + +static const struct snd_kcontrol_new o010_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN10, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN10, 30, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN10_1, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN10_2, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN10_2, 16, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I188 Switch", AFE_CONN10_5, 28, 1, 0), +}; + +static const struct snd_kcontrol_new o011_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN11, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN11, 31, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN11_1, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN11_2, 9, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN11_2, 17, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I189 Switch", AFE_CONN11_5, 29, 1, 0), +}; + +static const struct snd_kcontrol_new o012_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN12, 24, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN12_1, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN12_1, 16, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN12_2, 10, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN12_2, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I190 Switch", AFE_CONN12_5, 30, 1, 0), +}; + +static const struct snd_kcontrol_new o013_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN13, 25, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN13_1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN13_1, 17, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN13_2, 11, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN13_2, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I191 Switch", AFE_CONN13_5, 31, 1, 0), +}; + +static const struct snd_kcontrol_new o014_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN14, 26, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN14_1, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN14_1, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN14_2, 12, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN14_2, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I192 Switch", AFE_CONN14_6, 0, 1, 0), +}; + +static const struct snd_kcontrol_new o015_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN15, 27, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN15_1, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN15_1, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN15_2, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN15_2, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I193 Switch", AFE_CONN15_6, 1, 1, 0), +}; + +static const struct snd_kcontrol_new o016_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN16, 28, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN16_1, 4, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN16_1, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN16_2, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN16_2, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I194 Switch", AFE_CONN16_6, 2, 1, 0), +}; + +static const struct snd_kcontrol_new o017_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN17, 29, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN17_1, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN17_1, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN17_2, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN17_2, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I195 Switch", AFE_CONN17_6, 3, 1, 0), +}; + +static const struct snd_kcontrol_new o018_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN18_2, 16, 1, 0), +}; + +static const struct snd_kcontrol_new o019_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN19_2, 17, 1, 0), +}; + +static const struct snd_kcontrol_new o020_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN20_2, 18, 1, 0), +}; + +static const struct snd_kcontrol_new o021_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN21_2, 19, 1, 0), +}; + +static const struct snd_kcontrol_new o022_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN22_2, 20, 1, 0), +}; + +static const struct snd_kcontrol_new o023_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN23_2, 21, 1, 0), +}; + +static const struct snd_kcontrol_new o024_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN24_2, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o025_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN25_2, 23, 1, 0), +}; + +static const struct snd_kcontrol_new o026_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN26_1, 14, 1, 0), +}; + +static const struct snd_kcontrol_new o027_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN27_1, 15, 1, 0), +}; + +static const struct snd_kcontrol_new o028_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN28_1, 16, 1, 0), +}; + +static const struct snd_kcontrol_new o029_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN29_1, 17, 1, 0), +}; + +static const struct snd_kcontrol_new o030_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN30_1, 18, 1, 0), +}; + +static const struct snd_kcontrol_new o031_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN31_1, 19, 1, 0), +}; + +static const struct snd_kcontrol_new o032_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN32_1, 20, 1, 0), +}; + +static const struct snd_kcontrol_new o033_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN33_1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new o034_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN34, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN34, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN34, 12, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN34, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN34_2, 6, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN34_2, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN34_5, 8, 1, 0), +}; + +static const struct snd_kcontrol_new o035_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN35, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN35, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN35, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN35, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN35_2, 7, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN35_2, 9, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN35_5, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN35_5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new o036_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN36, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN36, 12, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN36, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN36_2, 6, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN36_5, 8, 1, 0), +}; + +static const struct snd_kcontrol_new o037_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN37, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN37, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN37, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN37_2, 7, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN37_5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new o038_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN38, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN38_5, 8, 1, 0), +}; + +static const struct snd_kcontrol_new o039_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN39, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN39_5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new o040_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN40, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN40, 12, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN40, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN40_5, 8, 1, 0), +}; + +static const struct snd_kcontrol_new o041_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN41, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN41, 13, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN41, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN41_5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new o042_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN42, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN42, 24, 1, 0), +}; + +static const struct snd_kcontrol_new o043_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN43, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN43, 25, 1, 0), +}; + +static const struct snd_kcontrol_new o044_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN44, 16, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN44, 26, 1, 0), +}; + +static const struct snd_kcontrol_new o045_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN45, 17, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN45, 27, 1, 0), +}; + +static const struct snd_kcontrol_new o046_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN46, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN46, 28, 1, 0), +}; + +static const struct snd_kcontrol_new o047_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN47, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN47, 29, 1, 0), +}; + +static const struct snd_kcontrol_new o182_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN182, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN182, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN182, 24, 1, 0), +}; + +static const struct snd_kcontrol_new o183_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN183, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN183, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN183, 25, 1, 0), +}; + +static const char * const dl8_dl11_data_sel_mux_text[] = { + "dl8", "dl11", +}; + +static SOC_ENUM_SINGLE_DECL(dl8_dl11_data_sel_mux_enum, + AFE_DAC_CON2, 0, dl8_dl11_data_sel_mux_text); + +static const struct snd_kcontrol_new dl8_dl11_data_sel_mux = + SOC_DAPM_ENUM("DL8_DL11 Sink", + dl8_dl11_data_sel_mux_enum); + +static const struct snd_soc_dapm_widget mt8188_memif_widgets[] = { + /* DL6 */ + SND_SOC_DAPM_MIXER("I000", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I001", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL3 */ + SND_SOC_DAPM_MIXER("I020", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I021", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL11 */ + SND_SOC_DAPM_MIXER("I022", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I023", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I024", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I025", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I026", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I027", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I028", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I029", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I030", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I031", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I034", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I035", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I036", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I037", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL11/DL8 */ + SND_SOC_DAPM_MIXER("I046", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I047", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I048", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I049", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I050", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I051", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I052", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I053", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I054", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I055", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I056", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I057", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I058", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I059", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I060", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I061", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL2 */ + SND_SOC_DAPM_MIXER("I070", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I071", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DL8_DL11 Mux", + SND_SOC_NOPM, 0, 0, &dl8_dl11_data_sel_mux), + + /* UL9 */ + SND_SOC_DAPM_MIXER("O002", SND_SOC_NOPM, 0, 0, + o002_mix, ARRAY_SIZE(o002_mix)), + SND_SOC_DAPM_MIXER("O003", SND_SOC_NOPM, 0, 0, + o003_mix, ARRAY_SIZE(o003_mix)), + SND_SOC_DAPM_MIXER("O004", SND_SOC_NOPM, 0, 0, + o004_mix, ARRAY_SIZE(o004_mix)), + SND_SOC_DAPM_MIXER("O005", SND_SOC_NOPM, 0, 0, + o005_mix, ARRAY_SIZE(o005_mix)), + SND_SOC_DAPM_MIXER("O006", SND_SOC_NOPM, 0, 0, + o006_mix, ARRAY_SIZE(o006_mix)), + SND_SOC_DAPM_MIXER("O007", SND_SOC_NOPM, 0, 0, + o007_mix, ARRAY_SIZE(o007_mix)), + SND_SOC_DAPM_MIXER("O008", SND_SOC_NOPM, 0, 0, + o008_mix, ARRAY_SIZE(o008_mix)), + SND_SOC_DAPM_MIXER("O009", SND_SOC_NOPM, 0, 0, + o009_mix, ARRAY_SIZE(o009_mix)), + SND_SOC_DAPM_MIXER("O010", SND_SOC_NOPM, 0, 0, + o010_mix, ARRAY_SIZE(o010_mix)), + SND_SOC_DAPM_MIXER("O011", SND_SOC_NOPM, 0, 0, + o011_mix, ARRAY_SIZE(o011_mix)), + SND_SOC_DAPM_MIXER("O012", SND_SOC_NOPM, 0, 0, + o012_mix, ARRAY_SIZE(o012_mix)), + SND_SOC_DAPM_MIXER("O013", SND_SOC_NOPM, 0, 0, + o013_mix, ARRAY_SIZE(o013_mix)), + SND_SOC_DAPM_MIXER("O014", SND_SOC_NOPM, 0, 0, + o014_mix, ARRAY_SIZE(o014_mix)), + SND_SOC_DAPM_MIXER("O015", SND_SOC_NOPM, 0, 0, + o015_mix, ARRAY_SIZE(o015_mix)), + SND_SOC_DAPM_MIXER("O016", SND_SOC_NOPM, 0, 0, + o016_mix, ARRAY_SIZE(o016_mix)), + SND_SOC_DAPM_MIXER("O017", SND_SOC_NOPM, 0, 0, + o017_mix, ARRAY_SIZE(o017_mix)), + SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, + o018_mix, ARRAY_SIZE(o018_mix)), + SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, + o019_mix, ARRAY_SIZE(o019_mix)), + SND_SOC_DAPM_MIXER("O020", SND_SOC_NOPM, 0, 0, + o020_mix, ARRAY_SIZE(o020_mix)), + SND_SOC_DAPM_MIXER("O021", SND_SOC_NOPM, 0, 0, + o021_mix, ARRAY_SIZE(o021_mix)), + SND_SOC_DAPM_MIXER("O022", SND_SOC_NOPM, 0, 0, + o022_mix, ARRAY_SIZE(o022_mix)), + SND_SOC_DAPM_MIXER("O023", SND_SOC_NOPM, 0, 0, + o023_mix, ARRAY_SIZE(o023_mix)), + SND_SOC_DAPM_MIXER("O024", SND_SOC_NOPM, 0, 0, + o024_mix, ARRAY_SIZE(o024_mix)), + SND_SOC_DAPM_MIXER("O025", SND_SOC_NOPM, 0, 0, + o025_mix, ARRAY_SIZE(o025_mix)), + SND_SOC_DAPM_MIXER("O026", SND_SOC_NOPM, 0, 0, + o026_mix, ARRAY_SIZE(o026_mix)), + SND_SOC_DAPM_MIXER("O027", SND_SOC_NOPM, 0, 0, + o027_mix, ARRAY_SIZE(o027_mix)), + SND_SOC_DAPM_MIXER("O028", SND_SOC_NOPM, 0, 0, + o028_mix, ARRAY_SIZE(o028_mix)), + SND_SOC_DAPM_MIXER("O029", SND_SOC_NOPM, 0, 0, + o029_mix, ARRAY_SIZE(o029_mix)), + SND_SOC_DAPM_MIXER("O030", SND_SOC_NOPM, 0, 0, + o030_mix, ARRAY_SIZE(o030_mix)), + SND_SOC_DAPM_MIXER("O031", SND_SOC_NOPM, 0, 0, + o031_mix, ARRAY_SIZE(o031_mix)), + SND_SOC_DAPM_MIXER("O032", SND_SOC_NOPM, 0, 0, + o032_mix, ARRAY_SIZE(o032_mix)), + SND_SOC_DAPM_MIXER("O033", SND_SOC_NOPM, 0, 0, + o033_mix, ARRAY_SIZE(o033_mix)), + + /* UL4 */ + SND_SOC_DAPM_MIXER("O034", SND_SOC_NOPM, 0, 0, + o034_mix, ARRAY_SIZE(o034_mix)), + SND_SOC_DAPM_MIXER("O035", SND_SOC_NOPM, 0, 0, + o035_mix, ARRAY_SIZE(o035_mix)), + + /* UL5 */ + SND_SOC_DAPM_MIXER("O036", SND_SOC_NOPM, 0, 0, + o036_mix, ARRAY_SIZE(o036_mix)), + SND_SOC_DAPM_MIXER("O037", SND_SOC_NOPM, 0, 0, + o037_mix, ARRAY_SIZE(o037_mix)), + + /* UL10 */ + SND_SOC_DAPM_MIXER("O038", SND_SOC_NOPM, 0, 0, + o038_mix, ARRAY_SIZE(o038_mix)), + SND_SOC_DAPM_MIXER("O039", SND_SOC_NOPM, 0, 0, + o039_mix, ARRAY_SIZE(o039_mix)), + SND_SOC_DAPM_MIXER("O182", SND_SOC_NOPM, 0, 0, + o182_mix, ARRAY_SIZE(o182_mix)), + SND_SOC_DAPM_MIXER("O183", SND_SOC_NOPM, 0, 0, + o183_mix, ARRAY_SIZE(o183_mix)), + + /* UL2 */ + SND_SOC_DAPM_MIXER("O040", SND_SOC_NOPM, 0, 0, + o040_mix, ARRAY_SIZE(o040_mix)), + SND_SOC_DAPM_MIXER("O041", SND_SOC_NOPM, 0, 0, + o041_mix, ARRAY_SIZE(o041_mix)), + SND_SOC_DAPM_MIXER("O042", SND_SOC_NOPM, 0, 0, + o042_mix, ARRAY_SIZE(o042_mix)), + SND_SOC_DAPM_MIXER("O043", SND_SOC_NOPM, 0, 0, + o043_mix, ARRAY_SIZE(o043_mix)), + SND_SOC_DAPM_MIXER("O044", SND_SOC_NOPM, 0, 0, + o044_mix, ARRAY_SIZE(o044_mix)), + SND_SOC_DAPM_MIXER("O045", SND_SOC_NOPM, 0, 0, + o045_mix, ARRAY_SIZE(o045_mix)), + SND_SOC_DAPM_MIXER("O046", SND_SOC_NOPM, 0, 0, + o046_mix, ARRAY_SIZE(o046_mix)), + SND_SOC_DAPM_MIXER("O047", SND_SOC_NOPM, 0, 0, + o047_mix, ARRAY_SIZE(o047_mix)), +}; + +static const struct snd_soc_dapm_route mt8188_memif_routes[] = { + {"I000", NULL, "DL6"}, + {"I001", NULL, "DL6"}, + + {"I020", NULL, "DL3"}, + {"I021", NULL, "DL3"}, + + {"I022", NULL, "DL11"}, + {"I023", NULL, "DL11"}, + {"I024", NULL, "DL11"}, + {"I025", NULL, "DL11"}, + {"I026", NULL, "DL11"}, + {"I027", NULL, "DL11"}, + {"I028", NULL, "DL11"}, + {"I029", NULL, "DL11"}, + {"I030", NULL, "DL11"}, + {"I031", NULL, "DL11"}, + {"I032", NULL, "DL11"}, + {"I033", NULL, "DL11"}, + {"I034", NULL, "DL11"}, + {"I035", NULL, "DL11"}, + {"I036", NULL, "DL11"}, + {"I037", NULL, "DL11"}, + + {"DL8_DL11 Mux", "dl8", "DL8"}, + {"DL8_DL11 Mux", "dl11", "DL11"}, + + {"I046", NULL, "DL8_DL11 Mux"}, + {"I047", NULL, "DL8_DL11 Mux"}, + {"I048", NULL, "DL8_DL11 Mux"}, + {"I049", NULL, "DL8_DL11 Mux"}, + {"I050", NULL, "DL8_DL11 Mux"}, + {"I051", NULL, "DL8_DL11 Mux"}, + {"I052", NULL, "DL8_DL11 Mux"}, + {"I053", NULL, "DL8_DL11 Mux"}, + {"I054", NULL, "DL8_DL11 Mux"}, + {"I055", NULL, "DL8_DL11 Mux"}, + {"I056", NULL, "DL8_DL11 Mux"}, + {"I057", NULL, "DL8_DL11 Mux"}, + {"I058", NULL, "DL8_DL11 Mux"}, + {"I059", NULL, "DL8_DL11 Mux"}, + {"I060", NULL, "DL8_DL11 Mux"}, + {"I061", NULL, "DL8_DL11 Mux"}, + + {"I070", NULL, "DL2"}, + {"I071", NULL, "DL2"}, + + {"UL9", NULL, "O002"}, + {"UL9", NULL, "O003"}, + {"UL9", NULL, "O004"}, + {"UL9", NULL, "O005"}, + {"UL9", NULL, "O006"}, + {"UL9", NULL, "O007"}, + {"UL9", NULL, "O008"}, + {"UL9", NULL, "O009"}, + {"UL9", NULL, "O010"}, + {"UL9", NULL, "O011"}, + {"UL9", NULL, "O012"}, + {"UL9", NULL, "O013"}, + {"UL9", NULL, "O014"}, + {"UL9", NULL, "O015"}, + {"UL9", NULL, "O016"}, + {"UL9", NULL, "O017"}, + {"UL9", NULL, "O018"}, + {"UL9", NULL, "O019"}, + {"UL9", NULL, "O020"}, + {"UL9", NULL, "O021"}, + {"UL9", NULL, "O022"}, + {"UL9", NULL, "O023"}, + {"UL9", NULL, "O024"}, + {"UL9", NULL, "O025"}, + {"UL9", NULL, "O026"}, + {"UL9", NULL, "O027"}, + {"UL9", NULL, "O028"}, + {"UL9", NULL, "O029"}, + {"UL9", NULL, "O030"}, + {"UL9", NULL, "O031"}, + {"UL9", NULL, "O032"}, + {"UL9", NULL, "O033"}, + + {"UL4", NULL, "O034"}, + {"UL4", NULL, "O035"}, + + {"UL5", NULL, "O036"}, + {"UL5", NULL, "O037"}, + + {"UL10", NULL, "O038"}, + {"UL10", NULL, "O039"}, + {"UL10", NULL, "O182"}, + {"UL10", NULL, "O183"}, + + {"UL2", NULL, "O040"}, + {"UL2", NULL, "O041"}, + {"UL2", NULL, "O042"}, + {"UL2", NULL, "O043"}, + {"UL2", NULL, "O044"}, + {"UL2", NULL, "O045"}, + {"UL2", NULL, "O046"}, + {"UL2", NULL, "O047"}, + + {"O004", "I000 Switch", "I000"}, + {"O005", "I001 Switch", "I001"}, + + {"O006", "I000 Switch", "I000"}, + {"O007", "I001 Switch", "I001"}, + + {"O010", "I022 Switch", "I022"}, + {"O011", "I023 Switch", "I023"}, + {"O012", "I024 Switch", "I024"}, + {"O013", "I025 Switch", "I025"}, + {"O014", "I026 Switch", "I026"}, + {"O015", "I027 Switch", "I027"}, + {"O016", "I028 Switch", "I028"}, + {"O017", "I029 Switch", "I029"}, + + {"O010", "I046 Switch", "I046"}, + {"O011", "I047 Switch", "I047"}, + {"O012", "I048 Switch", "I048"}, + {"O013", "I049 Switch", "I049"}, + {"O014", "I050 Switch", "I050"}, + {"O015", "I051 Switch", "I051"}, + {"O016", "I052 Switch", "I052"}, + {"O017", "I053 Switch", "I053"}, + + {"O002", "I022 Switch", "I022"}, + {"O003", "I023 Switch", "I023"}, + {"O004", "I024 Switch", "I024"}, + {"O005", "I025 Switch", "I025"}, + {"O006", "I026 Switch", "I026"}, + {"O007", "I027 Switch", "I027"}, + {"O008", "I028 Switch", "I028"}, + {"O009", "I029 Switch", "I029"}, + {"O010", "I030 Switch", "I030"}, + {"O011", "I031 Switch", "I031"}, + {"O012", "I032 Switch", "I032"}, + {"O013", "I033 Switch", "I033"}, + {"O014", "I034 Switch", "I034"}, + {"O015", "I035 Switch", "I035"}, + {"O016", "I036 Switch", "I036"}, + {"O017", "I037 Switch", "I037"}, + {"O026", "I046 Switch", "I046"}, + {"O027", "I047 Switch", "I047"}, + {"O028", "I048 Switch", "I048"}, + {"O029", "I049 Switch", "I049"}, + {"O030", "I050 Switch", "I050"}, + {"O031", "I051 Switch", "I051"}, + {"O032", "I052 Switch", "I052"}, + {"O033", "I053 Switch", "I053"}, + + {"O002", "I000 Switch", "I000"}, + {"O003", "I001 Switch", "I001"}, + {"O002", "I020 Switch", "I020"}, + {"O003", "I021 Switch", "I021"}, + {"O002", "I070 Switch", "I070"}, + {"O003", "I071 Switch", "I071"}, + + {"O034", "I000 Switch", "I000"}, + {"O035", "I001 Switch", "I001"}, + {"O034", "I002 Switch", "I002"}, + {"O035", "I003 Switch", "I003"}, + {"O034", "I012 Switch", "I012"}, + {"O035", "I013 Switch", "I013"}, + {"O034", "I020 Switch", "I020"}, + {"O035", "I021 Switch", "I021"}, + {"O034", "I070 Switch", "I070"}, + {"O035", "I071 Switch", "I071"}, + {"O034", "I072 Switch", "I072"}, + {"O035", "I073 Switch", "I073"}, + + {"O036", "I000 Switch", "I000"}, + {"O037", "I001 Switch", "I001"}, + {"O036", "I012 Switch", "I012"}, + {"O037", "I013 Switch", "I013"}, + {"O036", "I020 Switch", "I020"}, + {"O037", "I021 Switch", "I021"}, + {"O036", "I070 Switch", "I070"}, + {"O037", "I071 Switch", "I071"}, + {"O036", "I168 Switch", "I168"}, + {"O037", "I169 Switch", "I169"}, + + {"O038", "I022 Switch", "I022"}, + {"O039", "I023 Switch", "I023"}, + {"O182", "I024 Switch", "I024"}, + {"O183", "I025 Switch", "I025"}, + + {"O038", "I168 Switch", "I168"}, + {"O039", "I169 Switch", "I169"}, + + {"O182", "I020 Switch", "I020"}, + {"O183", "I021 Switch", "I021"}, + + {"O182", "I022 Switch", "I022"}, + {"O183", "I023 Switch", "I023"}, + + {"O040", "I022 Switch", "I022"}, + {"O041", "I023 Switch", "I023"}, + {"O042", "I024 Switch", "I024"}, + {"O043", "I025 Switch", "I025"}, + {"O044", "I026 Switch", "I026"}, + {"O045", "I027 Switch", "I027"}, + {"O046", "I028 Switch", "I028"}, + {"O047", "I029 Switch", "I029"}, + + {"O040", "I002 Switch", "I002"}, + {"O041", "I003 Switch", "I003"}, + + {"O002", "I012 Switch", "I012"}, + {"O003", "I013 Switch", "I013"}, + {"O004", "I014 Switch", "I014"}, + {"O005", "I015 Switch", "I015"}, + {"O006", "I016 Switch", "I016"}, + {"O007", "I017 Switch", "I017"}, + {"O008", "I018 Switch", "I018"}, + {"O009", "I019 Switch", "I019"}, + {"O010", "I188 Switch", "I188"}, + {"O011", "I189 Switch", "I189"}, + {"O012", "I190 Switch", "I190"}, + {"O013", "I191 Switch", "I191"}, + {"O014", "I192 Switch", "I192"}, + {"O015", "I193 Switch", "I193"}, + {"O016", "I194 Switch", "I194"}, + {"O017", "I195 Switch", "I195"}, + + {"O040", "I012 Switch", "I012"}, + {"O041", "I013 Switch", "I013"}, + {"O042", "I014 Switch", "I014"}, + {"O043", "I015 Switch", "I015"}, + {"O044", "I016 Switch", "I016"}, + {"O045", "I017 Switch", "I017"}, + {"O046", "I018 Switch", "I018"}, + {"O047", "I019 Switch", "I019"}, + + {"O002", "I072 Switch", "I072"}, + {"O003", "I073 Switch", "I073"}, + {"O004", "I074 Switch", "I074"}, + {"O005", "I075 Switch", "I075"}, + {"O006", "I076 Switch", "I076"}, + {"O007", "I077 Switch", "I077"}, + {"O008", "I078 Switch", "I078"}, + {"O009", "I079 Switch", "I079"}, + {"O010", "I080 Switch", "I080"}, + {"O011", "I081 Switch", "I081"}, + {"O012", "I082 Switch", "I082"}, + {"O013", "I083 Switch", "I083"}, + {"O014", "I084 Switch", "I084"}, + {"O015", "I085 Switch", "I085"}, + {"O016", "I086 Switch", "I086"}, + {"O017", "I087 Switch", "I087"}, + + {"O010", "I072 Switch", "I072"}, + {"O011", "I073 Switch", "I073"}, + {"O012", "I074 Switch", "I074"}, + {"O013", "I075 Switch", "I075"}, + {"O014", "I076 Switch", "I076"}, + {"O015", "I077 Switch", "I077"}, + {"O016", "I078 Switch", "I078"}, + {"O017", "I079 Switch", "I079"}, + {"O018", "I080 Switch", "I080"}, + {"O019", "I081 Switch", "I081"}, + {"O020", "I082 Switch", "I082"}, + {"O021", "I083 Switch", "I083"}, + {"O022", "I084 Switch", "I084"}, + {"O023", "I085 Switch", "I085"}, + {"O024", "I086 Switch", "I086"}, + {"O025", "I087 Switch", "I087"}, + + {"O002", "I168 Switch", "I168"}, + {"O003", "I169 Switch", "I169"}, + + {"O034", "I168 Switch", "I168"}, + {"O035", "I168 Switch", "I168"}, + {"O035", "I169 Switch", "I169"}, + + {"O040", "I168 Switch", "I168"}, + {"O041", "I169 Switch", "I169"}, +}; + +static const char * const mt8188_afe_1x_en_sel_text[] = { + "a1sys_a2sys", "a3sys", "a4sys", +}; + +static const unsigned int mt8188_afe_1x_en_sel_values[] = { + 0, 1, 2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dl2_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 18, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl3_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 20, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl6_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 22, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl7_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 24, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl8_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 26, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl10_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 28, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl11_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 30, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul1_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 0, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul2_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 2, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul3_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 4, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul4_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 6, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul5_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 8, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul6_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 10, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul8_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 12, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul9_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 14, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul10_1x_en_sel_enum, + A3_A4_TIMING_SEL1, 16, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq1_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 0, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq2_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 2, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq3_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 4, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq4_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 6, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq5_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 8, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq6_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 10, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq7_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 12, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq8_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 14, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq9_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 16, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq10_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 18, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq11_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 20, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq12_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 22, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq13_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 24, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq14_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 26, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq15_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 28, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq16_1x_en_sel_enum, + A3_A4_TIMING_SEL6, 30, 0x3, + mt8188_afe_1x_en_sel_text, + mt8188_afe_1x_en_sel_values); + +static const char * const mt8188_afe_fs_timing_sel_text[] = { + "asys", + "etdmout1_1x_en", + "etdmout2_1x_en", + "etdmout3_1x_en", + "etdmin1_1x_en", + "etdmin2_1x_en", + "etdmin1_nx_en", + "etdmin2_nx_en", +}; + +static const unsigned int mt8188_afe_fs_timing_sel_values[] = { + 0, + MT8188_ETDM_OUT1_1X_EN, + MT8188_ETDM_OUT2_1X_EN, + MT8188_ETDM_OUT3_1X_EN, + MT8188_ETDM_IN1_1X_EN, + MT8188_ETDM_IN2_1X_EN, + MT8188_ETDM_IN1_NX_EN, + MT8188_ETDM_IN2_NX_EN, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dl2_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl3_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl6_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl8_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(dl11_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul2_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul4_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul5_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul9_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ul10_fs_timing_sel_enum, + SND_SOC_NOPM, 0, 0, + mt8188_afe_fs_timing_sel_text, + mt8188_afe_fs_timing_sel_values); + +static int mt8188_memif_1x_en_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv; + unsigned int dai_id = kcontrol->id.device; + long val = ucontrol->value.integer.value[0]; + int ret = 0; + + memif_priv = afe_priv->dai_priv[dai_id]; + + if (val == memif_priv->asys_timing_sel) + return 0; + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + + memif_priv->asys_timing_sel = val; + + return ret; +} + +static int mt8188_asys_irq_1x_en_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + unsigned int id = kcontrol->id.device; + long val = ucontrol->value.integer.value[0]; + int ret = 0; + + if (val == afe_priv->irq_priv[id].asys_timing_sel) + return 0; + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + + afe_priv->irq_priv[id].asys_timing_sel = val; + + return ret; +} + +static int mt8188_memif_fs_timing_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv; + unsigned int dai_id = kcontrol->id.device; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + memif_priv = afe_priv->dai_priv[dai_id]; + + ucontrol->value.enumerated.item[0] = + snd_soc_enum_val_to_item(e, memif_priv->fs_timing); + + return 0; +} + +static int mt8188_memif_fs_timing_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv; + unsigned int dai_id = kcontrol->id.device; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int prev_item = 0; + + if (item[0] >= e->items) + return -EINVAL; + + memif_priv = afe_priv->dai_priv[dai_id]; + + prev_item = snd_soc_enum_val_to_item(e, memif_priv->fs_timing); + + if (item[0] == prev_item) + return 0; + + memif_priv->fs_timing = snd_soc_enum_item_to_val(e, item[0]); + + return 1; +} + +static const struct snd_kcontrol_new mt8188_memif_controls[] = { + MT8188_SOC_ENUM_EXT("dl2_1x_en_sel", + dl2_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL2), + MT8188_SOC_ENUM_EXT("dl3_1x_en_sel", + dl3_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL3), + MT8188_SOC_ENUM_EXT("dl6_1x_en_sel", + dl6_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL6), + MT8188_SOC_ENUM_EXT("dl7_1x_en_sel", + dl7_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL7), + MT8188_SOC_ENUM_EXT("dl8_1x_en_sel", + dl8_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL8), + MT8188_SOC_ENUM_EXT("dl10_1x_en_sel", + dl10_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL10), + MT8188_SOC_ENUM_EXT("dl11_1x_en_sel", + dl11_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_DL11), + MT8188_SOC_ENUM_EXT("ul1_1x_en_sel", + ul1_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL1), + MT8188_SOC_ENUM_EXT("ul2_1x_en_sel", + ul2_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL2), + MT8188_SOC_ENUM_EXT("ul3_1x_en_sel", + ul3_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL3), + MT8188_SOC_ENUM_EXT("ul4_1x_en_sel", + ul4_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL4), + MT8188_SOC_ENUM_EXT("ul5_1x_en_sel", + ul5_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL5), + MT8188_SOC_ENUM_EXT("ul6_1x_en_sel", + ul6_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL6), + MT8188_SOC_ENUM_EXT("ul8_1x_en_sel", + ul8_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL8), + MT8188_SOC_ENUM_EXT("ul9_1x_en_sel", + ul9_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL9), + MT8188_SOC_ENUM_EXT("ul10_1x_en_sel", + ul10_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_memif_1x_en_sel_put, + MT8188_AFE_MEMIF_UL10), + MT8188_SOC_ENUM_EXT("asys_irq1_1x_en_sel", + asys_irq1_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_13), + MT8188_SOC_ENUM_EXT("asys_irq2_1x_en_sel", + asys_irq2_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_14), + MT8188_SOC_ENUM_EXT("asys_irq3_1x_en_sel", + asys_irq3_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_15), + MT8188_SOC_ENUM_EXT("asys_irq4_1x_en_sel", + asys_irq4_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_16), + MT8188_SOC_ENUM_EXT("asys_irq5_1x_en_sel", + asys_irq5_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_17), + MT8188_SOC_ENUM_EXT("asys_irq6_1x_en_sel", + asys_irq6_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_18), + MT8188_SOC_ENUM_EXT("asys_irq7_1x_en_sel", + asys_irq7_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_19), + MT8188_SOC_ENUM_EXT("asys_irq8_1x_en_sel", + asys_irq8_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_20), + MT8188_SOC_ENUM_EXT("asys_irq9_1x_en_sel", + asys_irq9_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_21), + MT8188_SOC_ENUM_EXT("asys_irq10_1x_en_sel", + asys_irq10_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_22), + MT8188_SOC_ENUM_EXT("asys_irq11_1x_en_sel", + asys_irq11_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_23), + MT8188_SOC_ENUM_EXT("asys_irq12_1x_en_sel", + asys_irq12_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_24), + MT8188_SOC_ENUM_EXT("asys_irq13_1x_en_sel", + asys_irq13_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_25), + MT8188_SOC_ENUM_EXT("asys_irq14_1x_en_sel", + asys_irq14_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_26), + MT8188_SOC_ENUM_EXT("asys_irq15_1x_en_sel", + asys_irq15_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_27), + MT8188_SOC_ENUM_EXT("asys_irq16_1x_en_sel", + asys_irq16_1x_en_sel_enum, + snd_soc_get_enum_double, + mt8188_asys_irq_1x_en_sel_put, + MT8188_AFE_IRQ_28), + MT8188_SOC_ENUM_EXT("dl2_fs_timing_sel", + dl2_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_DL2), + MT8188_SOC_ENUM_EXT("dl3_fs_timing_sel", + dl3_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_DL3), + MT8188_SOC_ENUM_EXT("dl6_fs_timing_sel", + dl6_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_DL6), + MT8188_SOC_ENUM_EXT("dl8_fs_timing_sel", + dl8_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_DL8), + MT8188_SOC_ENUM_EXT("dl11_fs_timing_sel", + dl11_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_DL11), + MT8188_SOC_ENUM_EXT("ul2_fs_timing_sel", + ul2_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_UL2), + MT8188_SOC_ENUM_EXT("ul4_fs_timing_sel", + ul4_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_UL4), + MT8188_SOC_ENUM_EXT("ul5_fs_timing_sel", + ul5_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_UL5), + MT8188_SOC_ENUM_EXT("ul9_fs_timing_sel", + ul9_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_UL9), + MT8188_SOC_ENUM_EXT("ul10_fs_timing_sel", + ul10_fs_timing_sel_enum, + mt8188_memif_fs_timing_sel_get, + mt8188_memif_fs_timing_sel_put, + MT8188_AFE_MEMIF_UL10), +}; + +static const struct snd_soc_component_driver mt8188_afe_pcm_dai_component = { + .name = "mt8188-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT8188_AFE_MEMIF_NUM] = { + [MT8188_AFE_MEMIF_DL2] = { + .name = "DL2", + .id = MT8188_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .reg_ofs_end = AFE_DL2_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON0, + .fs_shift = 10, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 18, + .hd_reg = AFE_DL2_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 18, + .ch_num_reg = AFE_DL2_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x1f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 18, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 18, + }, + [MT8188_AFE_MEMIF_DL3] = { + .name = "DL3", + .id = MT8188_AFE_MEMIF_DL3, + .reg_ofs_base = AFE_DL3_BASE, + .reg_ofs_cur = AFE_DL3_CUR, + .reg_ofs_end = AFE_DL3_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON0, + .fs_shift = 15, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 19, + .hd_reg = AFE_DL3_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 19, + .ch_num_reg = AFE_DL3_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x1f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 19, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 19, + }, + [MT8188_AFE_MEMIF_DL6] = { + .name = "DL6", + .id = MT8188_AFE_MEMIF_DL6, + .reg_ofs_base = AFE_DL6_BASE, + .reg_ofs_cur = AFE_DL6_CUR, + .reg_ofs_end = AFE_DL6_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON1, + .fs_shift = 0, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 22, + .hd_reg = AFE_DL6_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 22, + .ch_num_reg = AFE_DL6_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x1f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 22, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 22, + }, + [MT8188_AFE_MEMIF_DL7] = { + .name = "DL7", + .id = MT8188_AFE_MEMIF_DL7, + .reg_ofs_base = AFE_DL7_BASE, + .reg_ofs_cur = AFE_DL7_CUR, + .reg_ofs_end = AFE_DL7_END, + .fs_reg = -1, + .fs_shift = 0, + .fs_maskbit = 0, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 23, + .hd_reg = AFE_DL7_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 23, + .ch_num_reg = AFE_DL7_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x1f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 23, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 23, + }, + [MT8188_AFE_MEMIF_DL8] = { + .name = "DL8", + .id = MT8188_AFE_MEMIF_DL8, + .reg_ofs_base = AFE_DL8_BASE, + .reg_ofs_cur = AFE_DL8_CUR, + .reg_ofs_end = AFE_DL8_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON1, + .fs_shift = 10, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 24, + .hd_reg = AFE_DL8_CON0, + .hd_shift = 6, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 24, + .ch_num_reg = AFE_DL8_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x3f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 24, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 24, + }, + [MT8188_AFE_MEMIF_DL10] = { + .name = "DL10", + .id = MT8188_AFE_MEMIF_DL10, + .reg_ofs_base = AFE_DL10_BASE, + .reg_ofs_cur = AFE_DL10_CUR, + .reg_ofs_end = AFE_DL10_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON1, + .fs_shift = 20, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 26, + .hd_reg = AFE_DL10_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 26, + .ch_num_reg = AFE_DL10_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x1f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 26, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 26, + }, + [MT8188_AFE_MEMIF_DL11] = { + .name = "DL11", + .id = MT8188_AFE_MEMIF_DL11, + .reg_ofs_base = AFE_DL11_BASE, + .reg_ofs_cur = AFE_DL11_CUR, + .reg_ofs_end = AFE_DL11_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON1, + .fs_shift = 25, + .fs_maskbit = 0x1f, + .mono_reg = -1, + .mono_shift = 0, + .int_odd_flag_reg = -1, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 27, + .hd_reg = AFE_DL11_CON0, + .hd_shift = 7, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 27, + .ch_num_reg = AFE_DL11_CON0, + .ch_num_shift = 0, + .ch_num_maskbit = 0x7f, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 27, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 27, + }, + [MT8188_AFE_MEMIF_UL1] = { + .name = "UL1", + .id = MT8188_AFE_MEMIF_UL1, + .reg_ofs_base = AFE_UL1_BASE, + .reg_ofs_cur = AFE_UL1_CUR, + .reg_ofs_end = AFE_UL1_END, + .fs_reg = -1, + .fs_shift = 0, + .fs_maskbit = 0, + .mono_reg = AFE_UL1_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL1_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 1, + .hd_reg = AFE_UL1_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 0, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 0, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 0, + }, + [MT8188_AFE_MEMIF_UL2] = { + .name = "UL2", + .id = MT8188_AFE_MEMIF_UL2, + .reg_ofs_base = AFE_UL2_BASE, + .reg_ofs_cur = AFE_UL2_CUR, + .reg_ofs_end = AFE_UL2_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON2, + .fs_shift = 5, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL2_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL2_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 2, + .hd_reg = AFE_UL2_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 1, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 1, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 1, + }, + [MT8188_AFE_MEMIF_UL3] = { + .name = "UL3", + .id = MT8188_AFE_MEMIF_UL3, + .reg_ofs_base = AFE_UL3_BASE, + .reg_ofs_cur = AFE_UL3_CUR, + .reg_ofs_end = AFE_UL3_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON2, + .fs_shift = 10, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL3_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL3_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 3, + .hd_reg = AFE_UL3_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 2, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 2, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 2, + }, + [MT8188_AFE_MEMIF_UL4] = { + .name = "UL4", + .id = MT8188_AFE_MEMIF_UL4, + .reg_ofs_base = AFE_UL4_BASE, + .reg_ofs_cur = AFE_UL4_CUR, + .reg_ofs_end = AFE_UL4_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON2, + .fs_shift = 15, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL4_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL4_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 4, + .hd_reg = AFE_UL4_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 3, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 3, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 3, + }, + [MT8188_AFE_MEMIF_UL5] = { + .name = "UL5", + .id = MT8188_AFE_MEMIF_UL5, + .reg_ofs_base = AFE_UL5_BASE, + .reg_ofs_cur = AFE_UL5_CUR, + .reg_ofs_end = AFE_UL5_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON2, + .fs_shift = 20, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL5_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL5_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 5, + .hd_reg = AFE_UL5_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 4, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 4, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 4, + }, + [MT8188_AFE_MEMIF_UL6] = { + .name = "UL6", + .id = MT8188_AFE_MEMIF_UL6, + .reg_ofs_base = AFE_UL6_BASE, + .reg_ofs_cur = AFE_UL6_CUR, + .reg_ofs_end = AFE_UL6_END, + .fs_reg = -1, + .fs_shift = 0, + .fs_maskbit = 0, + .mono_reg = AFE_UL6_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL6_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 6, + .hd_reg = AFE_UL6_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 5, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 5, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 5, + }, + [MT8188_AFE_MEMIF_UL8] = { + .name = "UL8", + .id = MT8188_AFE_MEMIF_UL8, + .reg_ofs_base = AFE_UL8_BASE, + .reg_ofs_cur = AFE_UL8_CUR, + .reg_ofs_end = AFE_UL8_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON3, + .fs_shift = 5, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL8_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL8_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 8, + .hd_reg = AFE_UL8_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 7, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 7, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 7, + }, + [MT8188_AFE_MEMIF_UL9] = { + .name = "UL9", + .id = MT8188_AFE_MEMIF_UL9, + .reg_ofs_base = AFE_UL9_BASE, + .reg_ofs_cur = AFE_UL9_CUR, + .reg_ofs_end = AFE_UL9_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON3, + .fs_shift = 10, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL9_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL9_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 9, + .hd_reg = AFE_UL9_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 8, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 8, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 8, + }, + [MT8188_AFE_MEMIF_UL10] = { + .name = "UL10", + .id = MT8188_AFE_MEMIF_UL10, + .reg_ofs_base = AFE_UL10_BASE, + .reg_ofs_cur = AFE_UL10_CUR, + .reg_ofs_end = AFE_UL10_END, + .fs_reg = AFE_MEMIF_AGENT_FS_CON3, + .fs_shift = 15, + .fs_maskbit = 0x1f, + .mono_reg = AFE_UL10_CON0, + .mono_shift = 1, + .int_odd_flag_reg = AFE_UL10_CON0, + .int_odd_flag_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = 10, + .hd_reg = AFE_UL10_CON0, + .hd_shift = 5, + .agent_disable_reg = AUDIO_TOP_CON5, + .agent_disable_shift = 9, + .ch_num_reg = -1, + .ch_num_shift = 0, + .ch_num_maskbit = 0, + .msb_reg = AFE_NORMAL_BASE_ADR_MSB, + .msb_shift = 9, + .msb_end_reg = AFE_NORMAL_END_ADR_MSB, + .msb_end_shift = 9, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT8188_AFE_IRQ_NUM] = { + [MT8188_AFE_IRQ_1] = { + .id = MT8188_AFE_IRQ_1, + .irq_cnt_reg = -1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ1_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 0, + .irq_status_shift = 16, + }, + [MT8188_AFE_IRQ_2] = { + .id = MT8188_AFE_IRQ_2, + .irq_cnt_reg = -1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ2_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 1, + .irq_status_shift = 17, + }, + [MT8188_AFE_IRQ_3] = { + .id = MT8188_AFE_IRQ_3, + .irq_cnt_reg = AFE_IRQ3_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ3_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 2, + .irq_status_shift = 18, + }, + [MT8188_AFE_IRQ_8] = { + .id = MT8188_AFE_IRQ_8, + .irq_cnt_reg = -1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ8_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 7, + .irq_status_shift = 23, + }, + [MT8188_AFE_IRQ_9] = { + .id = MT8188_AFE_IRQ_9, + .irq_cnt_reg = AFE_IRQ9_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ9_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 8, + .irq_status_shift = 24, + }, + [MT8188_AFE_IRQ_10] = { + .id = MT8188_AFE_IRQ_10, + .irq_cnt_reg = -1, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0, + .irq_fs_reg = -1, + .irq_fs_shift = 0, + .irq_fs_maskbit = 0, + .irq_en_reg = AFE_IRQ10_CON, + .irq_en_shift = 31, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = 9, + .irq_status_shift = 25, + }, + [MT8188_AFE_IRQ_13] = { + .id = MT8188_AFE_IRQ_13, + .irq_cnt_reg = ASYS_IRQ1_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ1_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ1_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 0, + .irq_status_shift = 0, + }, + [MT8188_AFE_IRQ_14] = { + .id = MT8188_AFE_IRQ_14, + .irq_cnt_reg = ASYS_IRQ2_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ2_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ2_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 1, + .irq_status_shift = 1, + }, + [MT8188_AFE_IRQ_15] = { + .id = MT8188_AFE_IRQ_15, + .irq_cnt_reg = ASYS_IRQ3_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ3_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ3_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 2, + .irq_status_shift = 2, + }, + [MT8188_AFE_IRQ_16] = { + .id = MT8188_AFE_IRQ_16, + .irq_cnt_reg = ASYS_IRQ4_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ4_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ4_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 3, + .irq_status_shift = 3, + }, + [MT8188_AFE_IRQ_17] = { + .id = MT8188_AFE_IRQ_17, + .irq_cnt_reg = ASYS_IRQ5_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ5_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ5_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 4, + .irq_status_shift = 4, + }, + [MT8188_AFE_IRQ_18] = { + .id = MT8188_AFE_IRQ_18, + .irq_cnt_reg = ASYS_IRQ6_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ6_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ6_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 5, + .irq_status_shift = 5, + }, + [MT8188_AFE_IRQ_19] = { + .id = MT8188_AFE_IRQ_19, + .irq_cnt_reg = ASYS_IRQ7_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ7_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ7_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 6, + .irq_status_shift = 6, + }, + [MT8188_AFE_IRQ_20] = { + .id = MT8188_AFE_IRQ_20, + .irq_cnt_reg = ASYS_IRQ8_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ8_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ8_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 7, + .irq_status_shift = 7, + }, + [MT8188_AFE_IRQ_21] = { + .id = MT8188_AFE_IRQ_21, + .irq_cnt_reg = ASYS_IRQ9_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ9_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ9_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 8, + .irq_status_shift = 8, + }, + [MT8188_AFE_IRQ_22] = { + .id = MT8188_AFE_IRQ_22, + .irq_cnt_reg = ASYS_IRQ10_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ10_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ10_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 9, + .irq_status_shift = 9, + }, + [MT8188_AFE_IRQ_23] = { + .id = MT8188_AFE_IRQ_23, + .irq_cnt_reg = ASYS_IRQ11_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ11_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ11_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 10, + .irq_status_shift = 10, + }, + [MT8188_AFE_IRQ_24] = { + .id = MT8188_AFE_IRQ_24, + .irq_cnt_reg = ASYS_IRQ12_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ12_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ12_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 11, + .irq_status_shift = 11, + }, + [MT8188_AFE_IRQ_25] = { + .id = MT8188_AFE_IRQ_25, + .irq_cnt_reg = ASYS_IRQ13_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ13_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ13_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 12, + .irq_status_shift = 12, + }, + [MT8188_AFE_IRQ_26] = { + .id = MT8188_AFE_IRQ_26, + .irq_cnt_reg = ASYS_IRQ14_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ14_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ14_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 13, + .irq_status_shift = 13, + }, + [MT8188_AFE_IRQ_27] = { + .id = MT8188_AFE_IRQ_27, + .irq_cnt_reg = ASYS_IRQ15_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ15_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ15_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 14, + .irq_status_shift = 14, + }, + [MT8188_AFE_IRQ_28] = { + .id = MT8188_AFE_IRQ_28, + .irq_cnt_reg = ASYS_IRQ16_CON, + .irq_cnt_shift = 0, + .irq_cnt_maskbit = 0xffffff, + .irq_fs_reg = ASYS_IRQ16_CON, + .irq_fs_shift = 24, + .irq_fs_maskbit = 0x1ffff, + .irq_en_reg = ASYS_IRQ16_CON, + .irq_en_shift = 31, + .irq_clr_reg = ASYS_IRQ_CLR, + .irq_clr_shift = 15, + .irq_status_shift = 15, + }, +}; + +static const int mt8188_afe_memif_const_irqs[MT8188_AFE_MEMIF_NUM] = { + [MT8188_AFE_MEMIF_DL2] = MT8188_AFE_IRQ_13, + [MT8188_AFE_MEMIF_DL3] = MT8188_AFE_IRQ_14, + [MT8188_AFE_MEMIF_DL6] = MT8188_AFE_IRQ_15, + [MT8188_AFE_MEMIF_DL7] = MT8188_AFE_IRQ_1, + [MT8188_AFE_MEMIF_DL8] = MT8188_AFE_IRQ_16, + [MT8188_AFE_MEMIF_DL10] = MT8188_AFE_IRQ_17, + [MT8188_AFE_MEMIF_DL11] = MT8188_AFE_IRQ_18, + [MT8188_AFE_MEMIF_UL1] = MT8188_AFE_IRQ_3, + [MT8188_AFE_MEMIF_UL2] = MT8188_AFE_IRQ_19, + [MT8188_AFE_MEMIF_UL3] = MT8188_AFE_IRQ_20, + [MT8188_AFE_MEMIF_UL4] = MT8188_AFE_IRQ_21, + [MT8188_AFE_MEMIF_UL5] = MT8188_AFE_IRQ_22, + [MT8188_AFE_MEMIF_UL6] = MT8188_AFE_IRQ_9, + [MT8188_AFE_MEMIF_UL8] = MT8188_AFE_IRQ_23, + [MT8188_AFE_MEMIF_UL9] = MT8188_AFE_IRQ_24, + [MT8188_AFE_MEMIF_UL10] = MT8188_AFE_IRQ_25, +}; + +static bool mt8188_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* these auto-gen reg has read-only bit, so put it as volatile */ + /* volatile reg cannot be cached, so cannot be set when power off */ + switch (reg) { + case AUDIO_TOP_CON0: + case AUDIO_TOP_CON1: + case AUDIO_TOP_CON3: + case AUDIO_TOP_CON4: + case AUDIO_TOP_CON5: + case AUDIO_TOP_CON6: + case ASYS_IRQ_CLR: + case ASYS_IRQ_STATUS: + case ASYS_IRQ_MON1: + case ASYS_IRQ_MON2: + case AFE_IRQ_MCU_CLR: + case AFE_IRQ_STATUS: + case AFE_IRQ3_CON_MON: + case AFE_IRQ_MCU_MON2: + case ADSP_IRQ_STATUS: + case AUDIO_TOP_STA0: + case AUDIO_TOP_STA1: + case AFE_GAIN1_CUR: + case AFE_GAIN2_CUR: + case AFE_IEC_BURST_INFO: + case AFE_IEC_CHL_STAT0: + case AFE_IEC_CHL_STAT1: + case AFE_IEC_CHR_STAT0: + case AFE_IEC_CHR_STAT1: + case AFE_SPDIFIN_CHSTS1: + case AFE_SPDIFIN_CHSTS2: + case AFE_SPDIFIN_CHSTS3: + case AFE_SPDIFIN_CHSTS4: + case AFE_SPDIFIN_CHSTS5: + case AFE_SPDIFIN_CHSTS6: + case AFE_SPDIFIN_DEBUG1: + case AFE_SPDIFIN_DEBUG2: + case AFE_SPDIFIN_DEBUG3: + case AFE_SPDIFIN_DEBUG4: + case AFE_SPDIFIN_EC: + case AFE_SPDIFIN_CKLOCK_CFG: + case AFE_SPDIFIN_BR_DBG1: + case AFE_SPDIFIN_CKFBDIV: + case AFE_SPDIFIN_INT_EXT: + case AFE_SPDIFIN_INT_EXT2: + case SPDIFIN_FREQ_STATUS: + case SPDIFIN_USERCODE1: + case SPDIFIN_USERCODE2: + case SPDIFIN_USERCODE3: + case SPDIFIN_USERCODE4: + case SPDIFIN_USERCODE5: + case SPDIFIN_USERCODE6: + case SPDIFIN_USERCODE7: + case SPDIFIN_USERCODE8: + case SPDIFIN_USERCODE9: + case SPDIFIN_USERCODE10: + case SPDIFIN_USERCODE11: + case SPDIFIN_USERCODE12: + case AFE_LINEIN_APLL_TUNER_MON: + case AFE_EARC_APLL_TUNER_MON: + case AFE_CM0_MON: + case AFE_CM1_MON: + case AFE_CM2_MON: + case AFE_MPHONE_MULTI_DET_MON0: + case AFE_MPHONE_MULTI_DET_MON1: + case AFE_MPHONE_MULTI_DET_MON2: + case AFE_MPHONE_MULTI2_DET_MON0: + case AFE_MPHONE_MULTI2_DET_MON1: + case AFE_MPHONE_MULTI2_DET_MON2: + case AFE_ADDA_MTKAIF_MON0: + case AFE_ADDA_MTKAIF_MON1: + case AFE_AUD_PAD_TOP: + case AFE_ADDA6_MTKAIF_MON0: + case AFE_ADDA6_MTKAIF_MON1: + case AFE_ADDA6_SRC_DEBUG_MON0: + case AFE_ADDA6_UL_SRC_MON0: + case AFE_ADDA6_UL_SRC_MON1: + case AFE_ASRC11_NEW_CON8: + case AFE_ASRC11_NEW_CON9: + case AFE_ASRC12_NEW_CON8: + case AFE_ASRC12_NEW_CON9: + case AFE_LRCK_CNT: + case AFE_DAC_MON0: + case AFE_DL2_CUR: + case AFE_DL3_CUR: + case AFE_DL6_CUR: + case AFE_DL7_CUR: + case AFE_DL8_CUR: + case AFE_DL10_CUR: + case AFE_DL11_CUR: + case AFE_UL1_CUR: + case AFE_UL2_CUR: + case AFE_UL3_CUR: + case AFE_UL4_CUR: + case AFE_UL5_CUR: + case AFE_UL6_CUR: + case AFE_UL8_CUR: + case AFE_UL9_CUR: + case AFE_UL10_CUR: + case AFE_DL8_CHK_SUM1: + case AFE_DL8_CHK_SUM2: + case AFE_DL8_CHK_SUM3: + case AFE_DL8_CHK_SUM4: + case AFE_DL8_CHK_SUM5: + case AFE_DL8_CHK_SUM6: + case AFE_DL10_CHK_SUM1: + case AFE_DL10_CHK_SUM2: + case AFE_DL10_CHK_SUM3: + case AFE_DL10_CHK_SUM4: + case AFE_DL10_CHK_SUM5: + case AFE_DL10_CHK_SUM6: + case AFE_DL11_CHK_SUM1: + case AFE_DL11_CHK_SUM2: + case AFE_DL11_CHK_SUM3: + case AFE_DL11_CHK_SUM4: + case AFE_DL11_CHK_SUM5: + case AFE_DL11_CHK_SUM6: + case AFE_UL1_CHK_SUM1: + case AFE_UL1_CHK_SUM2: + case AFE_UL2_CHK_SUM1: + case AFE_UL2_CHK_SUM2: + case AFE_UL3_CHK_SUM1: + case AFE_UL3_CHK_SUM2: + case AFE_UL4_CHK_SUM1: + case AFE_UL4_CHK_SUM2: + case AFE_UL5_CHK_SUM1: + case AFE_UL5_CHK_SUM2: + case AFE_UL6_CHK_SUM1: + case AFE_UL6_CHK_SUM2: + case AFE_UL8_CHK_SUM1: + case AFE_UL8_CHK_SUM2: + case AFE_DL2_CHK_SUM1: + case AFE_DL2_CHK_SUM2: + case AFE_DL3_CHK_SUM1: + case AFE_DL3_CHK_SUM2: + case AFE_DL6_CHK_SUM1: + case AFE_DL6_CHK_SUM2: + case AFE_DL7_CHK_SUM1: + case AFE_DL7_CHK_SUM2: + case AFE_UL9_CHK_SUM1: + case AFE_UL9_CHK_SUM2: + case AFE_BUS_MON1: + case UL1_MOD2AGT_CNT_LAT: + case UL2_MOD2AGT_CNT_LAT: + case UL3_MOD2AGT_CNT_LAT: + case UL4_MOD2AGT_CNT_LAT: + case UL5_MOD2AGT_CNT_LAT: + case UL6_MOD2AGT_CNT_LAT: + case UL8_MOD2AGT_CNT_LAT: + case UL9_MOD2AGT_CNT_LAT: + case UL10_MOD2AGT_CNT_LAT: + case AFE_MEMIF_BUF_FULL_MON: + case AFE_MEMIF_BUF_MON1: + case AFE_MEMIF_BUF_MON3: + case AFE_MEMIF_BUF_MON4: + case AFE_MEMIF_BUF_MON5: + case AFE_MEMIF_BUF_MON6: + case AFE_MEMIF_BUF_MON7: + case AFE_MEMIF_BUF_MON8: + case AFE_MEMIF_BUF_MON9: + case AFE_MEMIF_BUF_MON10: + case DL2_AGENT2MODULE_CNT: + case DL3_AGENT2MODULE_CNT: + case DL6_AGENT2MODULE_CNT: + case DL7_AGENT2MODULE_CNT: + case DL8_AGENT2MODULE_CNT: + case DL10_AGENT2MODULE_CNT: + case DL11_AGENT2MODULE_CNT: + case UL1_MODULE2AGENT_CNT: + case UL2_MODULE2AGENT_CNT: + case UL3_MODULE2AGENT_CNT: + case UL4_MODULE2AGENT_CNT: + case UL5_MODULE2AGENT_CNT: + case UL6_MODULE2AGENT_CNT: + case UL8_MODULE2AGENT_CNT: + case UL9_MODULE2AGENT_CNT: + case UL10_MODULE2AGENT_CNT: + case AFE_DMIC0_SRC_DEBUG_MON0: + case AFE_DMIC0_UL_SRC_MON0: + case AFE_DMIC0_UL_SRC_MON1: + case AFE_DMIC1_SRC_DEBUG_MON0: + case AFE_DMIC1_UL_SRC_MON0: + case AFE_DMIC1_UL_SRC_MON1: + case AFE_DMIC2_SRC_DEBUG_MON0: + case AFE_DMIC2_UL_SRC_MON0: + case AFE_DMIC2_UL_SRC_MON1: + case AFE_DMIC3_SRC_DEBUG_MON0: + case AFE_DMIC3_UL_SRC_MON0: + case AFE_DMIC3_UL_SRC_MON1: + case DMIC_GAIN1_CUR: + case DMIC_GAIN2_CUR: + case DMIC_GAIN3_CUR: + case DMIC_GAIN4_CUR: + case ETDM_IN1_MONITOR: + case ETDM_IN2_MONITOR: + case ETDM_OUT1_MONITOR: + case ETDM_OUT2_MONITOR: + case ETDM_OUT3_MONITOR: + case AFE_ADDA_SRC_DEBUG_MON0: + case AFE_ADDA_SRC_DEBUG_MON1: + case AFE_ADDA_DL_SDM_FIFO_MON: + case AFE_ADDA_DL_SRC_LCH_MON: + case AFE_ADDA_DL_SRC_RCH_MON: + case AFE_ADDA_DL_SDM_OUT_MON: + case AFE_GASRC0_NEW_CON8: + case AFE_GASRC0_NEW_CON9: + case AFE_GASRC0_NEW_CON12: + case AFE_GASRC1_NEW_CON8: + case AFE_GASRC1_NEW_CON9: + case AFE_GASRC1_NEW_CON12: + case AFE_GASRC2_NEW_CON8: + case AFE_GASRC2_NEW_CON9: + case AFE_GASRC2_NEW_CON12: + case AFE_GASRC3_NEW_CON8: + case AFE_GASRC3_NEW_CON9: + case AFE_GASRC3_NEW_CON12: + case AFE_GASRC4_NEW_CON8: + case AFE_GASRC4_NEW_CON9: + case AFE_GASRC4_NEW_CON12: + case AFE_GASRC5_NEW_CON8: + case AFE_GASRC5_NEW_CON9: + case AFE_GASRC5_NEW_CON12: + case AFE_GASRC6_NEW_CON8: + case AFE_GASRC6_NEW_CON9: + case AFE_GASRC6_NEW_CON12: + case AFE_GASRC7_NEW_CON8: + case AFE_GASRC7_NEW_CON9: + case AFE_GASRC7_NEW_CON12: + case AFE_GASRC8_NEW_CON8: + case AFE_GASRC8_NEW_CON9: + case AFE_GASRC8_NEW_CON12: + case AFE_GASRC9_NEW_CON8: + case AFE_GASRC9_NEW_CON9: + case AFE_GASRC9_NEW_CON12: + case AFE_GASRC10_NEW_CON8: + case AFE_GASRC10_NEW_CON9: + case AFE_GASRC10_NEW_CON12: + case AFE_GASRC11_NEW_CON8: + case AFE_GASRC11_NEW_CON9: + case AFE_GASRC11_NEW_CON12: + return true; + default: + return false; + }; +} + +static const struct regmap_config mt8188_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .volatile_reg = mt8188_is_volatile_reg, + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), + .cache_type = REGCACHE_FLAT, +}; + +#define AFE_IRQ_CLR_BITS (0x387) +#define ASYS_IRQ_CLR_BITS (0xffff) + +static irqreturn_t mt8188_afe_irq_handler(int irq_id, void *dev_id) +{ + struct mtk_base_afe *afe = dev_id; + unsigned int val = 0; + unsigned int asys_irq_clr_bits = 0; + unsigned int afe_irq_clr_bits = 0; + unsigned int irq_status_bits = 0; + unsigned int irq_clr_bits = 0; + unsigned int mcu_irq_mask = 0; + int i = 0; + int ret = 0; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &val); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + afe_irq_clr_bits = AFE_IRQ_CLR_BITS; + asys_irq_clr_bits = ASYS_IRQ_CLR_BITS; + goto err_irq; + } + + ret = regmap_read(afe->regmap, AFE_IRQ_MASK, &mcu_irq_mask); + if (ret) { + dev_err(afe->dev, "%s read irq mask err\n", __func__); + afe_irq_clr_bits = AFE_IRQ_CLR_BITS; + asys_irq_clr_bits = ASYS_IRQ_CLR_BITS; + goto err_irq; + } + + /* only clr cpu irq */ + val &= mcu_irq_mask; + + for (i = 0; i < MT8188_AFE_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + struct mtk_base_irq_data const *irq_data; + + if (memif->irq_usage < 0) + continue; + + irq_data = afe->irqs[memif->irq_usage].irq_data; + + irq_status_bits = BIT(irq_data->irq_status_shift); + irq_clr_bits = BIT(irq_data->irq_clr_shift); + + if (!(val & irq_status_bits)) + continue; + + if (irq_data->irq_clr_reg == ASYS_IRQ_CLR) + asys_irq_clr_bits |= irq_clr_bits; + else + afe_irq_clr_bits |= irq_clr_bits; + + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + if (asys_irq_clr_bits) + regmap_write(afe->regmap, ASYS_IRQ_CLR, asys_irq_clr_bits); + if (afe_irq_clr_bits) + regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, afe_irq_clr_bits); + + return IRQ_HANDLED; +} + +static int mt8188_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + mt8188_afe_disable_main_clock(afe); + + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + +skip_regmap: + mt8188_afe_disable_reg_rw_clk(afe); + + return 0; +} + +static int mt8188_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct arm_smccc_res res; + + arm_smccc_smc(MTK_SIP_AUDIO_CONTROL, + MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS, + 0, 0, 0, 0, 0, 0, &res); + + mt8188_afe_enable_reg_rw_clk(afe); + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + regcache_cache_only(afe->regmap, false); + regcache_sync(afe->regmap); + + mt8188_afe_enable_main_clock(afe); +skip_regmap: + return 0; +} + +static int mt8188_afe_component_probe(struct snd_soc_component *component) +{ + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + int ret; + + snd_soc_component_init_regmap(component, afe->regmap); + + ret = mtk_afe_add_sub_dai_control(component); + + return ret; +} + +static const struct snd_soc_component_driver mt8188_afe_component = { + .name = AFE_PCM_NAME, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, + .probe = mt8188_afe_component_probe, +}; + +static int init_memif_priv_data(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv; + int i; + + for (i = MT8188_AFE_MEMIF_START; i < MT8188_AFE_MEMIF_END; i++) { + memif_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_dai_memif_priv), + GFP_KERNEL); + if (!memif_priv) + return -ENOMEM; + + afe_priv->dai_priv[i] = memif_priv; + } + + return 0; +} + +static int mt8188_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt8188_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt8188_memif_dai_driver); + + dai->dapm_widgets = mt8188_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt8188_memif_widgets); + dai->dapm_routes = mt8188_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt8188_memif_routes); + dai->controls = mt8188_memif_controls; + dai->num_controls = ARRAY_SIZE(mt8188_memif_controls); + + return init_memif_priv_data(afe); +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt8188_dai_adda_register, + mt8188_dai_etdm_register, + mt8188_dai_pcm_register, + mt8188_dai_memif_register, +}; + +static const struct reg_sequence mt8188_afe_reg_defaults[] = { + { AFE_IRQ_MASK, 0x387ffff }, + { AFE_IRQ3_CON, BIT(30) }, + { AFE_IRQ9_CON, BIT(30) }, + { ETDM_IN1_CON4, 0x12000100 }, + { ETDM_IN2_CON4, 0x12000100 }, +}; + +static const struct reg_sequence mt8188_cg_patch[] = { + { AUDIO_TOP_CON0, 0xfffffffb }, + { AUDIO_TOP_CON1, 0xfffffff8 }, +}; + +static int mt8188_afe_init_registers(struct mtk_base_afe *afe) +{ + return regmap_multi_reg_write(afe->regmap, + mt8188_afe_reg_defaults, + ARRAY_SIZE(mt8188_afe_reg_defaults)); +} + +static int mt8188_afe_parse_of(struct mtk_base_afe *afe, + struct device_node *np) +{ +#if IS_ENABLED(CONFIG_SND_SOC_MT6359) + struct mt8188_afe_private *afe_priv = afe->platform_priv; + + afe_priv->topckgen = syscon_regmap_lookup_by_phandle(afe->dev->of_node, + "mediatek,topckgen"); + if (IS_ERR(afe_priv->topckgen)) + return dev_err_probe(afe->dev, PTR_ERR(afe_priv->topckgen), + "%s() Cannot find topckgen controller\n", + __func__); +#endif + return 0; +} + +static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt8188_afe_private *afe_priv; + struct device *dev; + int i, irq_id, ret; + struct snd_soc_component *component; + struct reset_control *rstc; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + dev = afe->dev; + + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return dev_err_probe(dev, PTR_ERR(afe->base_addr), + "AFE base_addr not found\n"); + + /* reset controller to reset audio regs before regmap cache */ + rstc = devm_reset_control_get_exclusive(dev, "audiosys"); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "could not get audiosys reset\n"); + + ret = reset_control_reset(rstc); + if (ret) { + dev_err(dev, "failed to trigger audio reset:%d\n", ret); + return ret; + } + + /* initial audio related clock */ + ret = mt8188_afe_init_clock(afe); + if (ret) + return dev_err_probe(dev, ret, "init clock error"); + + ret = devm_add_action_or_reset(dev, mt8188_afe_deinit_clock, (void *)afe); + if (ret) + return ret; + + spin_lock_init(&afe_priv->afe_ctrl_lock); + + mutex_init(&afe->irq_alloc_lock); + + /* irq initialize */ + afe->irqs_size = MT8188_AFE_IRQ_NUM; + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), + GFP_KERNEL); + if (!afe->irqs) + return -ENOMEM; + + for (i = 0; i < afe->irqs_size; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + /* init memif */ + afe->memif_size = MT8188_AFE_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + return -ENOMEM; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = mt8188_afe_memif_const_irqs[i]; + afe->memif[i].const_irq = 1; + afe->irqs[afe->memif[i].irq_usage].irq_occupyed = true; + } + + /* request irq */ + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) + return dev_err_probe(dev, irq_id, "no irq found"); + + ret = devm_request_irq(dev, irq_id, mt8188_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) + return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n"); + + /* init sub_dais */ + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) + return dev_err_probe(dev, ret, "dai register i %d fail\n", i); + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) + return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n"); + + afe->mtk_afe_hardware = &mt8188_afe_hardware; + afe->memif_fs = mt8188_memif_fs; + afe->irq_fs = mt8188_irq_fs; + + afe->runtime_resume = mt8188_afe_runtime_resume; + afe->runtime_suspend = mt8188_afe_runtime_suspend; + + platform_set_drvdata(pdev, afe); + + ret = mt8188_afe_parse_of(afe, pdev->dev.of_node); + if (ret) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* enable clock for regcache get default value from hw */ + afe_priv->pm_runtime_bypass_reg_ctl = true; + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to resume device\n"); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt8188_afe_regmap_config); + if (IS_ERR(afe->regmap)) { + ret = PTR_ERR(afe->regmap); + goto err_pm_put; + } + + ret = regmap_register_patch(afe->regmap, mt8188_cg_patch, + ARRAY_SIZE(mt8188_cg_patch)); + if (ret < 0) { + dev_info(dev, "Failed to apply cg patch\n"); + goto err_pm_put; + } + + /* register component */ + ret = devm_snd_soc_register_component(dev, &mt8188_afe_component, + NULL, 0); + if (ret) { + dev_warn(dev, "err_platform\n"); + goto err_pm_put; + } + + component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL); + if (!component) { + ret = -ENOMEM; + goto err_pm_put; + } + + ret = snd_soc_component_initialize(component, + &mt8188_afe_pcm_dai_component, + &pdev->dev); + if (ret) + goto err_pm_put; +#ifdef CONFIG_DEBUG_FS + component->debugfs_prefix = "pcm"; +#endif + ret = snd_soc_add_component(component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) { + dev_warn(dev, "err_add_component\n"); + goto err_pm_put; + } + + mt8188_afe_init_registers(afe); + + pm_runtime_put_sync(&pdev->dev); + afe_priv->pm_runtime_bypass_reg_ctl = false; + + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + + return 0; +err_pm_put: + pm_runtime_put_sync(dev); + + return ret; +} + +static int mt8188_afe_pcm_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct of_device_id mt8188_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8188-afe", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt8188_afe_pcm_dt_match); + +static const struct dev_pm_ops mt8188_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt8188_afe_runtime_suspend, + mt8188_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt8188_afe_pcm_driver = { + .driver = { + .name = "mt8188-audio", + .of_match_table = mt8188_afe_pcm_dt_match, + .pm = &mt8188_afe_pm_ops, + }, + .probe = mt8188_afe_pcm_dev_probe, + .remove = mt8188_afe_pcm_dev_remove, +}; + +module_platform_driver(mt8188_afe_pcm_driver); + +MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA 8188"); +MODULE_AUTHOR("Chun-Chia.Chiu <chun-chia.chiu@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c new file mode 100644 index 000000000000..be1c53bf4729 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt8188-audsys-clk.c -- MediaTek 8188 audsys clock control + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include "mt8188-afe-common.h" +#include "mt8188-audsys-clk.h" +#include "mt8188-audsys-clkid.h" +#include "mt8188-reg.h" + +struct afe_gate { + int id; + const char *name; + const char *parent_name; + int reg; + u8 bit; + const struct clk_ops *ops; + unsigned long flags; + u8 cg_flags; +}; + +#define GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, _flags, _cgflags) {\ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .reg = _reg, \ + .bit = _bit, \ + .flags = _flags, \ + .cg_flags = _cgflags, \ + } + +#define GATE_AFE(_id, _name, _parent, _reg, _bit) \ + GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, \ + CLK_SET_RATE_PARENT, CLK_GATE_SET_TO_DISABLE) + +#define GATE_AUD0(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON0, _bit) + +#define GATE_AUD1(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON1, _bit) + +#define GATE_AUD3(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON3, _bit) + +#define GATE_AUD4(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON4, _bit) + +#define GATE_AUD5(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON5, _bit) + +#define GATE_AUD6(_id, _name, _parent, _bit) \ + GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON6, _bit) + +static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = { + /* AUD0 */ + GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2), + GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4), + GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10), + GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11), + GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18), + GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19), + GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20), + GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21), + GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23), + GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24), + GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25), + GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26), + GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27), + GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28), + GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31), + + /* AUD1 */ + GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2), + GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10), + GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11), + GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12), + GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13), + GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14), + GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16), + GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17), + + /* AUD3 */ + GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5), + GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7), + + /* AUD4 */ + GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0), + GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1), + GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6), + GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7), + GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8), + GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16), + GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17), + GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19), + GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20), + GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21), + GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys", 22), + GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24), + GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys", 30), + GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys", 31), + + /* AUD5 */ + GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0), + GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1), + GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2), + GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3), + GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4), + GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5), + GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7), + GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8), + GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9), + GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18), + GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19), + GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22), + GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23), + GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24), + GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26), + GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27), + + /* AUD6 */ + GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0), + GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1), + GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2), + GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3), + GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4), + GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5), + GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6), + GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7), + GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8), + GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9), + GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10), + GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11), +}; + +int mt8188_audsys_clk_register(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct clk *clk; + struct clk_lookup *cl; + int i; + + afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK, + sizeof(*afe_priv->lookup), + GFP_KERNEL); + + if (!afe_priv->lookup) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { + const struct afe_gate *gate = &aud_clks[i]; + + clk = clk_register_gate(afe->dev, gate->name, gate->parent_name, + gate->flags, afe->base_addr + gate->reg, + gate->bit, gate->cg_flags, NULL); + + if (IS_ERR(clk)) { + dev_err(afe->dev, "Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */ + cl = kzalloc(sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->clk = clk; + cl->con_id = gate->name; + cl->dev_id = dev_name(afe->dev); + cl->clk_hw = NULL; + clkdev_add(cl); + + afe_priv->lookup[i] = cl; + } + + return 0; +} + +void mt8188_audsys_clk_unregister(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct clk *clk; + struct clk_lookup *cl; + int i; + + if (!afe_priv) + return; + + for (i = 0; i < CLK_AUD_NR_CLK; i++) { + cl = afe_priv->lookup[i]; + if (!cl) + continue; + + clk = cl->clk; + clk_unregister_gate(clk); + + clkdev_drop(cl); + } +} diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h new file mode 100644 index 000000000000..6c5f463ad7e4 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8188-audsys-clk.h -- MediaTek 8188 audsys clock definition + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#ifndef _MT8188_AUDSYS_CLK_H_ +#define _MT8188_AUDSYS_CLK_H_ + +int mt8188_audsys_clk_register(struct mtk_base_afe *afe); +void mt8188_audsys_clk_unregister(struct mtk_base_afe *afe); + +#endif diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h new file mode 100644 index 000000000000..6f34ffc760e0 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8188-audsys-clkid.h -- MediaTek 8188 audsys clock id definition + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#ifndef _MT8188_AUDSYS_CLKID_H_ +#define _MT8188_AUDSYS_CLKID_H_ + +enum{ + CLK_AUD_AFE, + CLK_AUD_LRCK_CNT, + CLK_AUD_SPDIFIN_TUNER_APLL, + CLK_AUD_SPDIFIN_TUNER_DBG, + CLK_AUD_UL_TML, + CLK_AUD_APLL1_TUNER, + CLK_AUD_APLL2_TUNER, + CLK_AUD_TOP0_SPDF, + CLK_AUD_APLL, + CLK_AUD_APLL2, + CLK_AUD_DAC, + CLK_AUD_DAC_PREDIS, + CLK_AUD_TML, + CLK_AUD_ADC, + CLK_AUD_DAC_HIRES, + CLK_AUD_A1SYS_HP, + CLK_AUD_AFE_DMIC1, + CLK_AUD_AFE_DMIC2, + CLK_AUD_AFE_DMIC3, + CLK_AUD_AFE_DMIC4, + CLK_AUD_AFE_26M_DMIC_TM, + CLK_AUD_UL_TML_HIRES, + CLK_AUD_ADC_HIRES, + CLK_AUD_LINEIN_TUNER, + CLK_AUD_EARC_TUNER, + CLK_AUD_I2SIN, + CLK_AUD_TDM_IN, + CLK_AUD_I2S_OUT, + CLK_AUD_TDM_OUT, + CLK_AUD_HDMI_OUT, + CLK_AUD_ASRC11, + CLK_AUD_ASRC12, + CLK_AUD_MULTI_IN, + CLK_AUD_INTDIR, + CLK_AUD_A1SYS, + CLK_AUD_A2SYS, + CLK_AUD_PCMIF, + CLK_AUD_A3SYS, + CLK_AUD_A4SYS, + CLK_AUD_MEMIF_UL1, + CLK_AUD_MEMIF_UL2, + CLK_AUD_MEMIF_UL3, + CLK_AUD_MEMIF_UL4, + CLK_AUD_MEMIF_UL5, + CLK_AUD_MEMIF_UL6, + CLK_AUD_MEMIF_UL8, + CLK_AUD_MEMIF_UL9, + CLK_AUD_MEMIF_UL10, + CLK_AUD_MEMIF_DL2, + CLK_AUD_MEMIF_DL3, + CLK_AUD_MEMIF_DL6, + CLK_AUD_MEMIF_DL7, + CLK_AUD_MEMIF_DL8, + CLK_AUD_MEMIF_DL10, + CLK_AUD_MEMIF_DL11, + CLK_AUD_GASRC0, + CLK_AUD_GASRC1, + CLK_AUD_GASRC2, + CLK_AUD_GASRC3, + CLK_AUD_GASRC4, + CLK_AUD_GASRC5, + CLK_AUD_GASRC6, + CLK_AUD_GASRC7, + CLK_AUD_GASRC8, + CLK_AUD_GASRC9, + CLK_AUD_GASRC10, + CLK_AUD_GASRC11, + CLK_AUD_NR_CLK, +}; + +#endif diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c new file mode 100644 index 000000000000..d71696901553 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI ADDA Control + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include "mt8188-afe-clk.h" +#include "mt8188-afe-common.h" +#include "mt8188-reg.h" + +#define ADDA_HIRES_THRES 48000 + +enum { + SUPPLY_SEQ_CLOCK_SEL, + SUPPLY_SEQ_ADDA_DL_ON, + SUPPLY_SEQ_ADDA_MTKAIF_CFG, + SUPPLY_SEQ_ADDA_UL_ON, + SUPPLY_SEQ_ADDA_AFE_ON, +}; + +enum { + MTK_AFE_ADDA_DL_RATE_8K = 0, + MTK_AFE_ADDA_DL_RATE_11K = 1, + MTK_AFE_ADDA_DL_RATE_12K = 2, + MTK_AFE_ADDA_DL_RATE_16K = 3, + MTK_AFE_ADDA_DL_RATE_22K = 4, + MTK_AFE_ADDA_DL_RATE_24K = 5, + MTK_AFE_ADDA_DL_RATE_32K = 6, + MTK_AFE_ADDA_DL_RATE_44K = 7, + MTK_AFE_ADDA_DL_RATE_48K = 8, + MTK_AFE_ADDA_DL_RATE_96K = 9, + MTK_AFE_ADDA_DL_RATE_192K = 10, +}; + +enum { + MTK_AFE_ADDA_UL_RATE_8K = 0, + MTK_AFE_ADDA_UL_RATE_16K = 1, + MTK_AFE_ADDA_UL_RATE_32K = 2, + MTK_AFE_ADDA_UL_RATE_48K = 3, + MTK_AFE_ADDA_UL_RATE_96K = 4, + MTK_AFE_ADDA_UL_RATE_192K = 5, +}; + +enum { + DELAY_DATA_MISO1 = 0, + DELAY_DATA_MISO0 = 1, +}; + +struct mtk_dai_adda_priv { + unsigned int dl_rate; + unsigned int ul_rate; +}; + +static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_DL_RATE_8K; + case 11025: + return MTK_AFE_ADDA_DL_RATE_11K; + case 12000: + return MTK_AFE_ADDA_DL_RATE_12K; + case 16000: + return MTK_AFE_ADDA_DL_RATE_16K; + case 22050: + return MTK_AFE_ADDA_DL_RATE_22K; + case 24000: + return MTK_AFE_ADDA_DL_RATE_24K; + case 32000: + return MTK_AFE_ADDA_DL_RATE_32K; + case 44100: + return MTK_AFE_ADDA_DL_RATE_44K; + case 48000: + return MTK_AFE_ADDA_DL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_DL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_DL_RATE_192K; + default: + dev_info(afe->dev, "%s(), rate %u invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_DL_RATE_48K; + } +} + +static unsigned int afe_adda_ul_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_UL_RATE_8K; + case 16000: + return MTK_AFE_ADDA_UL_RATE_16K; + case 32000: + return MTK_AFE_ADDA_UL_RATE_32K; + case 48000: + return MTK_AFE_ADDA_UL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_UL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_UL_RATE_192K; + default: + dev_info(afe->dev, "%s(), rate %u invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_UL_RATE_48K; + } +} + +static int mt8188_adda_mtkaif_init(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtkaif_param *param = &afe_priv->mtkaif_params; + int delay_data; + int delay_cycle; + unsigned int mask = 0; + unsigned int val = 0; + + /* set rx protocol 2 & mtkaif_rxif_clkinv_adc inverse */ + regmap_set_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0, + MTKAIF_RXIF_CLKINV_ADC | MTKAIF_RXIF_PROTOCOL2); + + regmap_set_bits(afe->regmap, AFE_AUD_PAD_TOP, RG_RX_PROTOCOL2); + + if (!param->mtkaif_calibration_ok) { + dev_info(afe->dev, "%s(), calibration fail\n", __func__); + return 0; + } + + /* set delay for ch1, ch2 */ + if (param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] >= + param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1]) { + delay_data = DELAY_DATA_MISO1; + delay_cycle = + param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] - + param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1]; + } else { + delay_data = DELAY_DATA_MISO0; + delay_cycle = + param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] - + param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0]; + } + + val = 0; + mask = (MTKAIF_RXIF_DELAY_DATA | MTKAIF_RXIF_DELAY_CYCLE_MASK); + val |= FIELD_PREP(MTKAIF_RXIF_DELAY_CYCLE_MASK, delay_cycle); + val |= FIELD_PREP(MTKAIF_RXIF_DELAY_DATA, delay_data); + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG2, mask, val); + + return 0; +} + +static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt8188_adda_mtkaif_init(afe); + break; + default: + break; + } + + return 0; +} + +static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */ + usleep_range(125, 135); + break; + default: + break; + } + + return 0; +} + +static void mtk_adda_ul_mictype(struct mtk_base_afe *afe, bool dmic) +{ + unsigned int reg = AFE_ADDA_UL_SRC_CON0; + unsigned int val; + + val = (UL_SDM3_LEVEL_CTL | UL_MODE_3P25M_CH1_CTL | + UL_MODE_3P25M_CH2_CTL); + + /* turn on dmic, ch1, ch2 */ + if (dmic) + regmap_set_bits(afe->regmap, reg, val); + else + regmap_clear_bits(afe->regmap, reg, val); +} + +static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtkaif_param *param = &afe_priv->mtkaif_params; + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mtk_adda_ul_mictype(afe, param->mtkaif_dmic_on); + break; + case SND_SOC_DAPM_POST_PMD: + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */ + usleep_range(125, 135); + break; + default: + break; + } + + return 0; +} + +static int mtk_audio_hires_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct clk *clk = afe_priv->clk[MT8188_CLK_TOP_AUDIO_H_SEL]; + struct clk *clk_parent; + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + clk_parent = afe_priv->clk[MT8188_CLK_APMIXED_APLL1]; + break; + case SND_SOC_DAPM_POST_PMD: + clk_parent = afe_priv->clk[MT8188_CLK_XTAL_26M]; + break; + default: + return 0; + } + mt8188_afe_set_clk_parent(afe, clk, clk_parent); + + return 0; +} + +static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = source; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_adda_priv *adda_priv; + + adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; + + if (!adda_priv) { + dev_err(afe->dev, "%s adda_priv == NULL", __func__); + return 0; + } + + return !!(adda_priv->ul_rate > ADDA_HIRES_THRES); +} + +static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = source; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_adda_priv *adda_priv; + + adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; + + if (!adda_priv) { + dev_err(afe->dev, "%s adda_priv == NULL", __func__); + return 0; + } + + return !!(adda_priv->dl_rate > ADDA_HIRES_THRES); +} + +static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN176, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN176, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN176, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN176, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN176_2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_adda_o177_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN177, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN177, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN177, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN177, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN177_2, 7, 1, 0), +}; + +static const char * const adda_dlgain_mux_map[] = { + "Bypass", "Connect", +}; + +static SOC_ENUM_SINGLE_DECL(adda_dlgain_mux_map_enum, + SND_SOC_NOPM, 0, + adda_dlgain_mux_map); + +static const struct snd_kcontrol_new adda_dlgain_mux_control = + SOC_DAPM_ENUM("DL_GAIN_MUX", adda_dlgain_mux_map_enum); + +static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { + SND_SOC_DAPM_MIXER("I168", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I169", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O176", SND_SOC_NOPM, 0, 0, + mtk_dai_adda_o176_mix, + ARRAY_SIZE(mtk_dai_adda_o176_mix)), + SND_SOC_DAPM_MIXER("O177", SND_SOC_NOPM, 0, 0, + mtk_dai_adda_o177_mix, + ARRAY_SIZE(mtk_dai_adda_o177_mix)), + + SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON, + AFE_ADDA_UL_DL_CON0, + ADDA_AFE_ON_SHIFT, 0, + NULL, + 0), + + SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON, + AFE_ADDA_DL_SRC2_CON0, + DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT, 0, + mtk_adda_dl_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON, + AFE_ADDA_UL_SRC_CON0, + UL_SRC_ON_TMP_CTL_SHIFT, 0, + mtk_adda_ul_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("AUDIO_HIRES", SUPPLY_SEQ_CLOCK_SEL, + SND_SOC_NOPM, + 0, 0, + mtk_audio_hires_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG, + SND_SOC_NOPM, + 0, 0, + mtk_adda_mtkaif_cfg_event, + SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_MUX("DL_GAIN_MUX", SND_SOC_NOPM, 0, 0, + &adda_dlgain_mux_control), + + SND_SOC_DAPM_PGA("DL_GAIN", AFE_ADDA_DL_SRC2_CON0, + DL_2_GAIN_ON_CTL_PRE_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("ADDA_INPUT"), + SND_SOC_DAPM_OUTPUT("ADDA_OUTPUT"), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_hires"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_hires"), +}; + +static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { + {"ADDA Capture", NULL, "ADDA Enable"}, + {"ADDA Capture", NULL, "ADDA Capture Enable"}, + {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"}, + {"ADDA Capture", NULL, "aud_adc"}, + {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect}, + {"aud_adc_hires", NULL, "AUDIO_HIRES"}, + + {"I168", NULL, "ADDA Capture"}, + {"I169", NULL, "ADDA Capture"}, + + {"ADDA Playback", NULL, "ADDA Enable"}, + {"ADDA Playback", NULL, "ADDA Playback Enable"}, + {"ADDA Playback", NULL, "aud_dac"}, + {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect}, + {"aud_dac_hires", NULL, "AUDIO_HIRES"}, + + {"DL_GAIN", NULL, "O176"}, + {"DL_GAIN", NULL, "O177"}, + + {"DL_GAIN_MUX", "Bypass", "O176"}, + {"DL_GAIN_MUX", "Bypass", "O177"}, + {"DL_GAIN_MUX", "Connect", "DL_GAIN"}, + + {"ADDA Playback", NULL, "DL_GAIN_MUX"}, + + {"O176", "I000 Switch", "I000"}, + {"O177", "I001 Switch", "I001"}, + + {"O176", "I002 Switch", "I002"}, + {"O177", "I003 Switch", "I003"}, + + {"O176", "I020 Switch", "I020"}, + {"O177", "I021 Switch", "I021"}, + + {"O176", "I022 Switch", "I022"}, + {"O177", "I023 Switch", "I023"}, + + {"O176", "I070 Switch", "I070"}, + {"O177", "I071 Switch", "I071"}, + + {"ADDA Capture", NULL, "ADDA_INPUT"}, + {"ADDA_OUTPUT", NULL, "ADDA Playback"}, +}; + +static int mt8188_adda_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtkaif_param *param = &afe_priv->mtkaif_params; + + ucontrol->value.integer.value[0] = param->mtkaif_dmic_on; + return 0; +} + +static int mt8188_adda_dmic_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtkaif_param *param = &afe_priv->mtkaif_params; + int dmic_on; + + dmic_on = !!ucontrol->value.integer.value[0]; + + dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n", + __func__, kcontrol->id.name, dmic_on); + + if (param->mtkaif_dmic_on == dmic_on) + return 0; + + param->mtkaif_dmic_on = dmic_on; + return 1; +} + +static const struct snd_kcontrol_new mtk_dai_adda_controls[] = { + SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC2_CON1, + DL_2_GAIN_CTL_PRE_SHIFT, 65535, 0), + SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0, + mt8188_adda_dmic_get, mt8188_adda_dmic_set), +}; + +static int mtk_dai_da_configure(struct mtk_base_afe *afe, + unsigned int rate, int id) +{ + unsigned int val = 0; + unsigned int mask = 0; + + /* set sampling rate */ + mask |= DL_2_INPUT_MODE_CTL_MASK; + val |= FIELD_PREP(DL_2_INPUT_MODE_CTL_MASK, + afe_adda_dl_rate_transform(afe, rate)); + + /* turn off saturation */ + mask |= DL_2_CH1_SATURATION_EN_CTL; + mask |= DL_2_CH2_SATURATION_EN_CTL; + + /* turn off mute function */ + mask |= DL_2_MUTE_CH1_OFF_CTL_PRE; + mask |= DL_2_MUTE_CH2_OFF_CTL_PRE; + val |= DL_2_MUTE_CH1_OFF_CTL_PRE; + val |= DL_2_MUTE_CH2_OFF_CTL_PRE; + + /* set voice input data if input sample rate is 8k or 16k */ + mask |= DL_2_VOICE_MODE_CTL_PRE; + if (rate == 8000 || rate == 16000) + val |= DL_2_VOICE_MODE_CTL_PRE; + + regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, mask, val); + + /* new 2nd sdm */ + regmap_set_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON, + DL_USE_NEW_2ND_SDM); + + return 0; +} + +static int mtk_dai_ad_configure(struct mtk_base_afe *afe, + unsigned int rate, int id) +{ + unsigned int val; + unsigned int mask; + + mask = UL_VOICE_MODE_CTL_MASK; + val = FIELD_PREP(UL_VOICE_MODE_CTL_MASK, + afe_adda_ul_rate_transform(afe, rate)); + + regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, + mask, val); + return 0; +} + +static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_adda_priv *adda_priv = afe_priv->dai_priv[dai->id]; + unsigned int rate = params_rate(params); + int id = dai->id; + int ret = 0; + + dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n", + __func__, id, substream->stream, rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + adda_priv->dl_rate = rate; + ret = mtk_dai_da_configure(afe, rate, id); + } else { + adda_priv->ul_rate = rate; + ret = mtk_dai_ad_configure(afe, rate, id); + } + + return ret; +} + +static const struct snd_soc_dai_ops mtk_dai_adda_ops = { + .hw_params = mtk_dai_adda_hw_params, +}; + +/* dai driver */ +#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { + { + .name = "ADDA", + .id = MT8188_AFE_IO_ADDA, + .playback = { + .stream_name = "ADDA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_PLAYBACK_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .capture = { + .stream_name = "ADDA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_CAPTURE_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .ops = &mtk_dai_adda_ops, + }, +}; + +static int init_adda_priv_data(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_adda_priv *adda_priv; + + adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv), + GFP_KERNEL); + if (!adda_priv) + return -ENOMEM; + + afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv; + + return 0; +} + +int mt8188_dai_adda_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_adda_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + + dai->dapm_widgets = mtk_dai_adda_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); + dai->dapm_routes = mtk_dai_adda_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); + dai->controls = mtk_dai_adda_controls; + dai->num_controls = ARRAY_SIZE(mtk_dai_adda_controls); + + return init_adda_priv_data(afe); +} diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c new file mode 100644 index 000000000000..071841903c62 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c @@ -0,0 +1,2573 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI eTDM Control + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8188-afe-clk.h" +#include "mt8188-afe-common.h" +#include "mt8188-reg.h" + +#define MT8188_ETDM_MAX_CHANNELS 16 +#define MT8188_ETDM_NORMAL_MAX_BCK_RATE 24576000 +#define ETDM_TO_DAI_ID(x) ((x) + MT8188_AFE_IO_ETDM_START) +#define ENUM_TO_STR(x) #x + +enum { + MTK_DAI_ETDM_FORMAT_I2S = 0, + MTK_DAI_ETDM_FORMAT_LJ, + MTK_DAI_ETDM_FORMAT_RJ, + MTK_DAI_ETDM_FORMAT_EIAJ, + MTK_DAI_ETDM_FORMAT_DSPA, + MTK_DAI_ETDM_FORMAT_DSPB, +}; + +enum { + MTK_DAI_ETDM_DATA_ONE_PIN = 0, + MTK_DAI_ETDM_DATA_MULTI_PIN, +}; + +enum { + ETDM_IN, + ETDM_OUT, +}; + +enum { + COWORK_ETDM_NONE = 0, + COWORK_ETDM_IN1_M = 2, + COWORK_ETDM_IN1_S = 3, + COWORK_ETDM_IN2_M = 4, + COWORK_ETDM_IN2_S = 5, + COWORK_ETDM_OUT1_M = 10, + COWORK_ETDM_OUT1_S = 11, + COWORK_ETDM_OUT2_M = 12, + COWORK_ETDM_OUT2_S = 13, + COWORK_ETDM_OUT3_M = 14, + COWORK_ETDM_OUT3_S = 15, +}; + +enum { + ETDM_RELATCH_TIMING_A1A2SYS, + ETDM_RELATCH_TIMING_A3SYS, + ETDM_RELATCH_TIMING_A4SYS, +}; + +enum { + ETDM_SYNC_NONE, + ETDM_SYNC_FROM_IN1 = 2, + ETDM_SYNC_FROM_IN2 = 4, + ETDM_SYNC_FROM_OUT1 = 10, + ETDM_SYNC_FROM_OUT2 = 12, + ETDM_SYNC_FROM_OUT3 = 14, +}; + +struct etdm_con_reg { + unsigned int con0; + unsigned int con1; + unsigned int con2; + unsigned int con3; + unsigned int con4; + unsigned int con5; +}; + +struct mtk_dai_etdm_rate { + unsigned int rate; + unsigned int reg_value; +}; + +struct mtk_dai_etdm_priv { + unsigned int clock_mode; + unsigned int data_mode; + bool slave_mode; + bool lrck_inv; + bool bck_inv; + unsigned int format; + unsigned int slots; + unsigned int lrck_width; + unsigned int mclk_freq; + unsigned int mclk_fixed_apll; + unsigned int mclk_apll; + unsigned int mclk_dir; + int cowork_source_id; //dai id + unsigned int cowork_slv_count; + int cowork_slv_id[MT8188_AFE_IO_ETDM_NUM - 1]; //dai_id + bool in_disable_ch[MT8188_ETDM_MAX_CHANNELS]; + unsigned int en_ref_cnt; + bool is_prepared; +}; + +static const struct mtk_dai_etdm_rate mt8188_etdm_rates[] = { + { .rate = 8000, .reg_value = 0, }, + { .rate = 12000, .reg_value = 1, }, + { .rate = 16000, .reg_value = 2, }, + { .rate = 24000, .reg_value = 3, }, + { .rate = 32000, .reg_value = 4, }, + { .rate = 48000, .reg_value = 5, }, + { .rate = 96000, .reg_value = 7, }, + { .rate = 192000, .reg_value = 9, }, + { .rate = 384000, .reg_value = 11, }, + { .rate = 11025, .reg_value = 16, }, + { .rate = 22050, .reg_value = 17, }, + { .rate = 44100, .reg_value = 18, }, + { .rate = 88200, .reg_value = 19, }, + { .rate = 176400, .reg_value = 20, }, + { .rate = 352800, .reg_value = 21, }, +}; + +static int get_etdm_fs_timing(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8188_etdm_rates); i++) + if (mt8188_etdm_rates[i].rate == rate) + return mt8188_etdm_rates[i].reg_value; + + return -EINVAL; +} + +static unsigned int get_etdm_ch_fixup(unsigned int channels) +{ + if (channels > 16) + return 24; + else if (channels > 8) + return 16; + else if (channels > 4) + return 8; + else if (channels > 2) + return 4; + else + return 2; +} + +static int get_etdm_reg(unsigned int dai_id, struct etdm_con_reg *etdm_reg) +{ + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + etdm_reg->con0 = ETDM_IN1_CON0; + etdm_reg->con1 = ETDM_IN1_CON1; + etdm_reg->con2 = ETDM_IN1_CON2; + etdm_reg->con3 = ETDM_IN1_CON3; + etdm_reg->con4 = ETDM_IN1_CON4; + etdm_reg->con5 = ETDM_IN1_CON5; + break; + case MT8188_AFE_IO_ETDM2_IN: + etdm_reg->con0 = ETDM_IN2_CON0; + etdm_reg->con1 = ETDM_IN2_CON1; + etdm_reg->con2 = ETDM_IN2_CON2; + etdm_reg->con3 = ETDM_IN2_CON3; + etdm_reg->con4 = ETDM_IN2_CON4; + etdm_reg->con5 = ETDM_IN2_CON5; + break; + case MT8188_AFE_IO_ETDM1_OUT: + etdm_reg->con0 = ETDM_OUT1_CON0; + etdm_reg->con1 = ETDM_OUT1_CON1; + etdm_reg->con2 = ETDM_OUT1_CON2; + etdm_reg->con3 = ETDM_OUT1_CON3; + etdm_reg->con4 = ETDM_OUT1_CON4; + etdm_reg->con5 = ETDM_OUT1_CON5; + break; + case MT8188_AFE_IO_ETDM2_OUT: + etdm_reg->con0 = ETDM_OUT2_CON0; + etdm_reg->con1 = ETDM_OUT2_CON1; + etdm_reg->con2 = ETDM_OUT2_CON2; + etdm_reg->con3 = ETDM_OUT2_CON3; + etdm_reg->con4 = ETDM_OUT2_CON4; + etdm_reg->con5 = ETDM_OUT2_CON5; + break; + case MT8188_AFE_IO_ETDM3_OUT: + case MT8188_AFE_IO_DPTX: + etdm_reg->con0 = ETDM_OUT3_CON0; + etdm_reg->con1 = ETDM_OUT3_CON1; + etdm_reg->con2 = ETDM_OUT3_CON2; + etdm_reg->con3 = ETDM_OUT3_CON3; + etdm_reg->con4 = ETDM_OUT3_CON4; + etdm_reg->con5 = ETDM_OUT3_CON5; + break; + default: + return -EINVAL; + } + return 0; +} + +static int get_etdm_dir(unsigned int dai_id) +{ + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + case MT8188_AFE_IO_ETDM2_IN: + return ETDM_IN; + case MT8188_AFE_IO_ETDM1_OUT: + case MT8188_AFE_IO_ETDM2_OUT: + case MT8188_AFE_IO_ETDM3_OUT: + return ETDM_OUT; + default: + return -EINVAL; + } +} + +static int get_etdm_wlen(unsigned int bitwidth) +{ + return bitwidth <= 16 ? 16 : 32; +} + +static bool is_valid_etdm_dai(int dai_id) +{ + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + fallthrough; + case MT8188_AFE_IO_ETDM2_IN: + fallthrough; + case MT8188_AFE_IO_ETDM1_OUT: + fallthrough; + case MT8188_AFE_IO_ETDM2_OUT: + fallthrough; + case MT8188_AFE_IO_DPTX: + fallthrough; + case MT8188_AFE_IO_ETDM3_OUT: + return true; + default: + return false; + } +} + +static int is_cowork_mode(struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + return (etdm_data->cowork_slv_count > 0 || + etdm_data->cowork_source_id != COWORK_ETDM_NONE); +} + +static int sync_to_dai_id(int source_sel) +{ + switch (source_sel) { + case ETDM_SYNC_FROM_IN1: + return MT8188_AFE_IO_ETDM1_IN; + case ETDM_SYNC_FROM_IN2: + return MT8188_AFE_IO_ETDM2_IN; + case ETDM_SYNC_FROM_OUT1: + return MT8188_AFE_IO_ETDM1_OUT; + case ETDM_SYNC_FROM_OUT2: + return MT8188_AFE_IO_ETDM2_OUT; + case ETDM_SYNC_FROM_OUT3: + return MT8188_AFE_IO_ETDM3_OUT; + default: + return 0; + } +} + +static int get_etdm_cowork_master_id(struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + int dai_id; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + dai_id = etdm_data->cowork_source_id; + + if (dai_id == COWORK_ETDM_NONE) + dai_id = dai->id; + + return dai_id; +} + +static int mtk_dai_etdm_get_cg_id_by_dai_id(int dai_id) +{ + switch (dai_id) { + case MT8188_AFE_IO_DPTX: + return MT8188_CLK_AUD_HDMI_OUT; + case MT8188_AFE_IO_ETDM1_IN: + return MT8188_CLK_AUD_TDM_IN; + case MT8188_AFE_IO_ETDM2_IN: + return MT8188_CLK_AUD_I2SIN; + case MT8188_AFE_IO_ETDM1_OUT: + return MT8188_CLK_AUD_TDM_OUT; + case MT8188_AFE_IO_ETDM2_OUT: + return MT8188_CLK_AUD_I2S_OUT; + case MT8188_AFE_IO_ETDM3_OUT: + return MT8188_CLK_AUD_HDMI_OUT; + default: + return -EINVAL; + } +} + +static int mtk_dai_etdm_get_clk_id_by_dai_id(int dai_id) +{ + switch (dai_id) { + case MT8188_AFE_IO_DPTX: + return MT8188_CLK_TOP_DPTX_M_SEL; + case MT8188_AFE_IO_ETDM1_IN: + return MT8188_CLK_TOP_I2SI1_M_SEL; + case MT8188_AFE_IO_ETDM2_IN: + return MT8188_CLK_TOP_I2SI2_M_SEL; + case MT8188_AFE_IO_ETDM1_OUT: + return MT8188_CLK_TOP_I2SO1_M_SEL; + case MT8188_AFE_IO_ETDM2_OUT: + return MT8188_CLK_TOP_I2SO2_M_SEL; + case MT8188_AFE_IO_ETDM3_OUT: + default: + return -EINVAL; + } +} + +static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id) +{ + switch (dai_id) { + case MT8188_AFE_IO_DPTX: + return MT8188_CLK_TOP_APLL12_DIV9; + case MT8188_AFE_IO_ETDM1_IN: + return MT8188_CLK_TOP_APLL12_DIV0; + case MT8188_AFE_IO_ETDM2_IN: + return MT8188_CLK_TOP_APLL12_DIV1; + case MT8188_AFE_IO_ETDM1_OUT: + return MT8188_CLK_TOP_APLL12_DIV2; + case MT8188_AFE_IO_ETDM2_OUT: + return MT8188_CLK_TOP_APLL12_DIV3; + case MT8188_AFE_IO_ETDM3_OUT: + default: + return -EINVAL; + } +} + +static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); + + if (clkdiv_id < 0) + return -EINVAL; + + mt8188_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]); + + return 0; +} + +static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); + + if (clkdiv_id < 0) + return -EINVAL; + + mt8188_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]); + + return 0; +} + +static const struct snd_kcontrol_new mtk_dai_etdm_o048_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN48, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN48, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN48_1, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN48_2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o049_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN49, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN49, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN49_1, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN49_2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o050_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN50, 24, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN50_1, 16, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o051_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN51, 25, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN51_1, 17, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o052_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN52, 26, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN52_1, 18, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o053_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN53, 27, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN53_1, 19, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o054_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN54, 28, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN54_1, 20, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o055_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN55, 29, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN55_1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o056_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN56, 30, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN56_1, 22, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o057_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN57, 31, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN57_1, 23, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o058_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN58_1, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN58_1, 24, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o059_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN59_1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN59_1, 25, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o060_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN60_1, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN60_1, 26, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o061_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN61_1, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN61_1, 27, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o062_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN62_1, 4, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN62_1, 28, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o063_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN63_1, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN63_1, 29, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o072_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN72, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN72, 22, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN72_1, 14, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN72_2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o073_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN73, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN73, 23, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN73_1, 15, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN73_2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o074_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN74, 24, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN74_1, 16, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o075_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN75, 25, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN75_1, 17, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o076_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN76, 26, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN76_1, 18, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o077_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN77, 27, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN77_1, 19, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o078_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN78, 28, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN78_1, 20, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o079_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN79, 29, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN79_1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o080_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN80, 30, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN80_1, 22, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o081_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN81, 31, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN81_1, 23, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o082_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN82_1, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN82_1, 24, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o083_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN83_1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN83_1, 25, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o084_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN84_1, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN84_1, 26, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o085_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN85_1, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN85_1, 27, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o086_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN86_1, 4, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN86_1, 28, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_etdm_o087_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN87_1, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN87_1, 29, 1, 0), +}; + +static const char * const mt8188_etdm_clk_src_sel_text[] = { + "26m", + "a1sys_a2sys", + "a3sys", + "a4sys", +}; + +static SOC_ENUM_SINGLE_EXT_DECL(etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_text); + +static const char * const hdmitx_dptx_mux_map[] = { + "Disconnect", "Connect", +}; + +static int hdmitx_dptx_mux_map_value[] = { + 0, 1, +}; + +/* HDMI_OUT_MUX */ +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum, + SND_SOC_NOPM, + 0, + 1, + hdmitx_dptx_mux_map, + hdmitx_dptx_mux_map_value); + +static const struct snd_kcontrol_new hdmi_out_mux_control = + SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum); + +/* DPTX_OUT_MUX */ +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum, + SND_SOC_NOPM, + 0, + 1, + hdmitx_dptx_mux_map, + hdmitx_dptx_mux_map_value); + +static const struct snd_kcontrol_new dptx_out_mux_control = + SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum); + +/* HDMI_CH0_MUX ~ HDMI_CH7_MUX */ +static const char *const afe_conn_hdmi_mux_map[] = { + "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", +}; + +static int afe_conn_hdmi_mux_map_value[] = { + 0, 1, 2, 3, 4, 5, 6, 7, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum, + AFE_TDMOUT_CONN0, + 0, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch0_mux_control = + SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum, + AFE_TDMOUT_CONN0, + 4, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch1_mux_control = + SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum, + AFE_TDMOUT_CONN0, + 8, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch2_mux_control = + SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum, + AFE_TDMOUT_CONN0, + 12, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch3_mux_control = + SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum, + AFE_TDMOUT_CONN0, + 16, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch4_mux_control = + SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum, + AFE_TDMOUT_CONN0, + 20, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch5_mux_control = + SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum, + AFE_TDMOUT_CONN0, + 24, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch6_mux_control = + SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum, + AFE_TDMOUT_CONN0, + 28, + 0xf, + afe_conn_hdmi_mux_map, + afe_conn_hdmi_mux_map_value); + +static const struct snd_kcontrol_new hdmi_ch7_mux_control = + SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum); + +static int mt8188_etdm_clk_src_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + unsigned int source = ucontrol->value.enumerated.item[0]; + unsigned int val; + unsigned int old_val; + unsigned int mask; + unsigned int reg; + unsigned int shift; + + if (source >= e->items) + return -EINVAL; + + if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) { + reg = ETDM_OUT1_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source); + } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) { + reg = ETDM_OUT2_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source); + } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) { + reg = ETDM_OUT3_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source); + } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) { + reg = ETDM_IN1_CON2; + mask = ETDM_IN_CON2_CLOCK_MASK; + shift = ETDM_IN_CON2_CLOCK_SHIFT; + val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source); + } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) { + reg = ETDM_IN2_CON2; + mask = ETDM_IN_CON2_CLOCK_MASK; + shift = ETDM_IN_CON2_CLOCK_SHIFT; + val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source); + } else { + return -EINVAL; + } + + regmap_read(afe->regmap, reg, &old_val); + old_val &= mask; + old_val >>= shift; + + if (old_val == val) + return 0; + + regmap_update_bits(afe->regmap, reg, mask, val); + + return 1; +} + +static int mt8188_etdm_clk_src_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + unsigned int value; + unsigned int reg; + unsigned int mask; + unsigned int shift; + + if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) { + reg = ETDM_OUT1_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) { + reg = ETDM_OUT2_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) { + reg = ETDM_OUT3_CON4; + mask = ETDM_OUT_CON4_CLOCK_MASK; + shift = ETDM_OUT_CON4_CLOCK_SHIFT; + } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) { + reg = ETDM_IN1_CON2; + mask = ETDM_IN_CON2_CLOCK_MASK; + shift = ETDM_IN_CON2_CLOCK_SHIFT; + } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) { + reg = ETDM_IN2_CON2; + mask = ETDM_IN_CON2_CLOCK_MASK; + shift = ETDM_IN_CON2_CLOCK_SHIFT; + } else { + return -EINVAL; + } + + regmap_read(afe->regmap, reg, &value); + + value &= mask; + value >>= shift; + ucontrol->value.enumerated.item[0] = value; + return 0; +} + +static const struct snd_kcontrol_new mtk_dai_etdm_controls[] = { + SOC_ENUM_EXT("ETDM_OUT1_Clock_Source", etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_get, + mt8188_etdm_clk_src_sel_put), + SOC_ENUM_EXT("ETDM_OUT2_Clock_Source", etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_get, + mt8188_etdm_clk_src_sel_put), + SOC_ENUM_EXT("ETDM_OUT3_Clock_Source", etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_get, + mt8188_etdm_clk_src_sel_put), + SOC_ENUM_EXT("ETDM_IN1_Clock_Source", etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_get, + mt8188_etdm_clk_src_sel_put), + SOC_ENUM_EXT("ETDM_IN2_Clock_Source", etdmout_clk_src_enum, + mt8188_etdm_clk_src_sel_get, + mt8188_etdm_clk_src_sel_put), +}; + +static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { + /* eTDM_IN2 */ + SND_SOC_DAPM_MIXER("I012", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I013", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I014", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I015", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I016", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I017", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I018", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I019", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I188", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I189", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I190", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I191", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I192", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I193", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I194", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I195", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* eTDM_IN1 */ + SND_SOC_DAPM_MIXER("I072", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I073", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I074", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I075", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I076", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I077", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I078", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I079", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I080", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I081", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I082", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I083", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I084", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I085", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I086", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I087", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* eTDM_OUT2 */ + SND_SOC_DAPM_MIXER("O048", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o048_mix, ARRAY_SIZE(mtk_dai_etdm_o048_mix)), + SND_SOC_DAPM_MIXER("O049", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o049_mix, ARRAY_SIZE(mtk_dai_etdm_o049_mix)), + SND_SOC_DAPM_MIXER("O050", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o050_mix, ARRAY_SIZE(mtk_dai_etdm_o050_mix)), + SND_SOC_DAPM_MIXER("O051", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o051_mix, ARRAY_SIZE(mtk_dai_etdm_o051_mix)), + SND_SOC_DAPM_MIXER("O052", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o052_mix, ARRAY_SIZE(mtk_dai_etdm_o052_mix)), + SND_SOC_DAPM_MIXER("O053", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o053_mix, ARRAY_SIZE(mtk_dai_etdm_o053_mix)), + SND_SOC_DAPM_MIXER("O054", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o054_mix, ARRAY_SIZE(mtk_dai_etdm_o054_mix)), + SND_SOC_DAPM_MIXER("O055", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o055_mix, ARRAY_SIZE(mtk_dai_etdm_o055_mix)), + SND_SOC_DAPM_MIXER("O056", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o056_mix, ARRAY_SIZE(mtk_dai_etdm_o056_mix)), + SND_SOC_DAPM_MIXER("O057", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o057_mix, ARRAY_SIZE(mtk_dai_etdm_o057_mix)), + SND_SOC_DAPM_MIXER("O058", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o058_mix, ARRAY_SIZE(mtk_dai_etdm_o058_mix)), + SND_SOC_DAPM_MIXER("O059", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o059_mix, ARRAY_SIZE(mtk_dai_etdm_o059_mix)), + SND_SOC_DAPM_MIXER("O060", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o060_mix, ARRAY_SIZE(mtk_dai_etdm_o060_mix)), + SND_SOC_DAPM_MIXER("O061", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o061_mix, ARRAY_SIZE(mtk_dai_etdm_o061_mix)), + SND_SOC_DAPM_MIXER("O062", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o062_mix, ARRAY_SIZE(mtk_dai_etdm_o062_mix)), + SND_SOC_DAPM_MIXER("O063", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o063_mix, ARRAY_SIZE(mtk_dai_etdm_o063_mix)), + + /* eTDM_OUT1 */ + SND_SOC_DAPM_MIXER("O072", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o072_mix, ARRAY_SIZE(mtk_dai_etdm_o072_mix)), + SND_SOC_DAPM_MIXER("O073", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o073_mix, ARRAY_SIZE(mtk_dai_etdm_o073_mix)), + SND_SOC_DAPM_MIXER("O074", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o074_mix, ARRAY_SIZE(mtk_dai_etdm_o074_mix)), + SND_SOC_DAPM_MIXER("O075", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o075_mix, ARRAY_SIZE(mtk_dai_etdm_o075_mix)), + SND_SOC_DAPM_MIXER("O076", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o076_mix, ARRAY_SIZE(mtk_dai_etdm_o076_mix)), + SND_SOC_DAPM_MIXER("O077", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o077_mix, ARRAY_SIZE(mtk_dai_etdm_o077_mix)), + SND_SOC_DAPM_MIXER("O078", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o078_mix, ARRAY_SIZE(mtk_dai_etdm_o078_mix)), + SND_SOC_DAPM_MIXER("O079", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o079_mix, ARRAY_SIZE(mtk_dai_etdm_o079_mix)), + SND_SOC_DAPM_MIXER("O080", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o080_mix, ARRAY_SIZE(mtk_dai_etdm_o080_mix)), + SND_SOC_DAPM_MIXER("O081", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o081_mix, ARRAY_SIZE(mtk_dai_etdm_o081_mix)), + SND_SOC_DAPM_MIXER("O082", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o082_mix, ARRAY_SIZE(mtk_dai_etdm_o082_mix)), + SND_SOC_DAPM_MIXER("O083", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o083_mix, ARRAY_SIZE(mtk_dai_etdm_o083_mix)), + SND_SOC_DAPM_MIXER("O084", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o084_mix, ARRAY_SIZE(mtk_dai_etdm_o084_mix)), + SND_SOC_DAPM_MIXER("O085", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o085_mix, ARRAY_SIZE(mtk_dai_etdm_o085_mix)), + SND_SOC_DAPM_MIXER("O086", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o086_mix, ARRAY_SIZE(mtk_dai_etdm_o086_mix)), + SND_SOC_DAPM_MIXER("O087", SND_SOC_NOPM, 0, 0, + mtk_dai_etdm_o087_mix, ARRAY_SIZE(mtk_dai_etdm_o087_mix)), + + /* eTDM_OUT3 */ + SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_out_mux_control), + SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0, + &dptx_out_mux_control), + + SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch0_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch1_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch2_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch3_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch4_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch5_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch6_mux_control), + SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0, + &hdmi_ch7_mux_control), + + SND_SOC_DAPM_INPUT("ETDM_INPUT"), + SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"), +}; + +static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { + {"I012", NULL, "ETDM2_IN"}, + {"I013", NULL, "ETDM2_IN"}, + {"I014", NULL, "ETDM2_IN"}, + {"I015", NULL, "ETDM2_IN"}, + {"I016", NULL, "ETDM2_IN"}, + {"I017", NULL, "ETDM2_IN"}, + {"I018", NULL, "ETDM2_IN"}, + {"I019", NULL, "ETDM2_IN"}, + {"I188", NULL, "ETDM2_IN"}, + {"I189", NULL, "ETDM2_IN"}, + {"I190", NULL, "ETDM2_IN"}, + {"I191", NULL, "ETDM2_IN"}, + {"I192", NULL, "ETDM2_IN"}, + {"I193", NULL, "ETDM2_IN"}, + {"I194", NULL, "ETDM2_IN"}, + {"I195", NULL, "ETDM2_IN"}, + + {"I072", NULL, "ETDM1_IN"}, + {"I073", NULL, "ETDM1_IN"}, + {"I074", NULL, "ETDM1_IN"}, + {"I075", NULL, "ETDM1_IN"}, + {"I076", NULL, "ETDM1_IN"}, + {"I077", NULL, "ETDM1_IN"}, + {"I078", NULL, "ETDM1_IN"}, + {"I079", NULL, "ETDM1_IN"}, + {"I080", NULL, "ETDM1_IN"}, + {"I081", NULL, "ETDM1_IN"}, + {"I082", NULL, "ETDM1_IN"}, + {"I083", NULL, "ETDM1_IN"}, + {"I084", NULL, "ETDM1_IN"}, + {"I085", NULL, "ETDM1_IN"}, + {"I086", NULL, "ETDM1_IN"}, + {"I087", NULL, "ETDM1_IN"}, + + {"UL8", NULL, "ETDM1_IN"}, + {"UL3", NULL, "ETDM2_IN"}, + + {"ETDM2_OUT", NULL, "O048"}, + {"ETDM2_OUT", NULL, "O049"}, + {"ETDM2_OUT", NULL, "O050"}, + {"ETDM2_OUT", NULL, "O051"}, + {"ETDM2_OUT", NULL, "O052"}, + {"ETDM2_OUT", NULL, "O053"}, + {"ETDM2_OUT", NULL, "O054"}, + {"ETDM2_OUT", NULL, "O055"}, + {"ETDM2_OUT", NULL, "O056"}, + {"ETDM2_OUT", NULL, "O057"}, + {"ETDM2_OUT", NULL, "O058"}, + {"ETDM2_OUT", NULL, "O059"}, + {"ETDM2_OUT", NULL, "O060"}, + {"ETDM2_OUT", NULL, "O061"}, + {"ETDM2_OUT", NULL, "O062"}, + {"ETDM2_OUT", NULL, "O063"}, + + {"ETDM1_OUT", NULL, "O072"}, + {"ETDM1_OUT", NULL, "O073"}, + {"ETDM1_OUT", NULL, "O074"}, + {"ETDM1_OUT", NULL, "O075"}, + {"ETDM1_OUT", NULL, "O076"}, + {"ETDM1_OUT", NULL, "O077"}, + {"ETDM1_OUT", NULL, "O078"}, + {"ETDM1_OUT", NULL, "O079"}, + {"ETDM1_OUT", NULL, "O080"}, + {"ETDM1_OUT", NULL, "O081"}, + {"ETDM1_OUT", NULL, "O082"}, + {"ETDM1_OUT", NULL, "O083"}, + {"ETDM1_OUT", NULL, "O084"}, + {"ETDM1_OUT", NULL, "O085"}, + {"ETDM1_OUT", NULL, "O086"}, + {"ETDM1_OUT", NULL, "O087"}, + + {"O048", "I020 Switch", "I020"}, + {"O049", "I021 Switch", "I021"}, + + {"O048", "I022 Switch", "I022"}, + {"O049", "I023 Switch", "I023"}, + {"O050", "I024 Switch", "I024"}, + {"O051", "I025 Switch", "I025"}, + {"O052", "I026 Switch", "I026"}, + {"O053", "I027 Switch", "I027"}, + {"O054", "I028 Switch", "I028"}, + {"O055", "I029 Switch", "I029"}, + {"O056", "I030 Switch", "I030"}, + {"O057", "I031 Switch", "I031"}, + {"O058", "I032 Switch", "I032"}, + {"O059", "I033 Switch", "I033"}, + {"O060", "I034 Switch", "I034"}, + {"O061", "I035 Switch", "I035"}, + {"O062", "I036 Switch", "I036"}, + {"O063", "I037 Switch", "I037"}, + + {"O048", "I046 Switch", "I046"}, + {"O049", "I047 Switch", "I047"}, + {"O050", "I048 Switch", "I048"}, + {"O051", "I049 Switch", "I049"}, + {"O052", "I050 Switch", "I050"}, + {"O053", "I051 Switch", "I051"}, + {"O054", "I052 Switch", "I052"}, + {"O055", "I053 Switch", "I053"}, + {"O056", "I054 Switch", "I054"}, + {"O057", "I055 Switch", "I055"}, + {"O058", "I056 Switch", "I056"}, + {"O059", "I057 Switch", "I057"}, + {"O060", "I058 Switch", "I058"}, + {"O061", "I059 Switch", "I059"}, + {"O062", "I060 Switch", "I060"}, + {"O063", "I061 Switch", "I061"}, + + {"O048", "I070 Switch", "I070"}, + {"O049", "I071 Switch", "I071"}, + + {"O072", "I020 Switch", "I020"}, + {"O073", "I021 Switch", "I021"}, + + {"O072", "I022 Switch", "I022"}, + {"O073", "I023 Switch", "I023"}, + {"O074", "I024 Switch", "I024"}, + {"O075", "I025 Switch", "I025"}, + {"O076", "I026 Switch", "I026"}, + {"O077", "I027 Switch", "I027"}, + {"O078", "I028 Switch", "I028"}, + {"O079", "I029 Switch", "I029"}, + {"O080", "I030 Switch", "I030"}, + {"O081", "I031 Switch", "I031"}, + {"O082", "I032 Switch", "I032"}, + {"O083", "I033 Switch", "I033"}, + {"O084", "I034 Switch", "I034"}, + {"O085", "I035 Switch", "I035"}, + {"O086", "I036 Switch", "I036"}, + {"O087", "I037 Switch", "I037"}, + + {"O072", "I046 Switch", "I046"}, + {"O073", "I047 Switch", "I047"}, + {"O074", "I048 Switch", "I048"}, + {"O075", "I049 Switch", "I049"}, + {"O076", "I050 Switch", "I050"}, + {"O077", "I051 Switch", "I051"}, + {"O078", "I052 Switch", "I052"}, + {"O079", "I053 Switch", "I053"}, + {"O080", "I054 Switch", "I054"}, + {"O081", "I055 Switch", "I055"}, + {"O082", "I056 Switch", "I056"}, + {"O083", "I057 Switch", "I057"}, + {"O084", "I058 Switch", "I058"}, + {"O085", "I059 Switch", "I059"}, + {"O086", "I060 Switch", "I060"}, + {"O087", "I061 Switch", "I061"}, + + {"O072", "I070 Switch", "I070"}, + {"O073", "I071 Switch", "I071"}, + + {"HDMI_CH0_MUX", "CH0", "DL10"}, + {"HDMI_CH0_MUX", "CH1", "DL10"}, + {"HDMI_CH0_MUX", "CH2", "DL10"}, + {"HDMI_CH0_MUX", "CH3", "DL10"}, + {"HDMI_CH0_MUX", "CH4", "DL10"}, + {"HDMI_CH0_MUX", "CH5", "DL10"}, + {"HDMI_CH0_MUX", "CH6", "DL10"}, + {"HDMI_CH0_MUX", "CH7", "DL10"}, + + {"HDMI_CH1_MUX", "CH0", "DL10"}, + {"HDMI_CH1_MUX", "CH1", "DL10"}, + {"HDMI_CH1_MUX", "CH2", "DL10"}, + {"HDMI_CH1_MUX", "CH3", "DL10"}, + {"HDMI_CH1_MUX", "CH4", "DL10"}, + {"HDMI_CH1_MUX", "CH5", "DL10"}, + {"HDMI_CH1_MUX", "CH6", "DL10"}, + {"HDMI_CH1_MUX", "CH7", "DL10"}, + + {"HDMI_CH2_MUX", "CH0", "DL10"}, + {"HDMI_CH2_MUX", "CH1", "DL10"}, + {"HDMI_CH2_MUX", "CH2", "DL10"}, + {"HDMI_CH2_MUX", "CH3", "DL10"}, + {"HDMI_CH2_MUX", "CH4", "DL10"}, + {"HDMI_CH2_MUX", "CH5", "DL10"}, + {"HDMI_CH2_MUX", "CH6", "DL10"}, + {"HDMI_CH2_MUX", "CH7", "DL10"}, + + {"HDMI_CH3_MUX", "CH0", "DL10"}, + {"HDMI_CH3_MUX", "CH1", "DL10"}, + {"HDMI_CH3_MUX", "CH2", "DL10"}, + {"HDMI_CH3_MUX", "CH3", "DL10"}, + {"HDMI_CH3_MUX", "CH4", "DL10"}, + {"HDMI_CH3_MUX", "CH5", "DL10"}, + {"HDMI_CH3_MUX", "CH6", "DL10"}, + {"HDMI_CH3_MUX", "CH7", "DL10"}, + + {"HDMI_CH4_MUX", "CH0", "DL10"}, + {"HDMI_CH4_MUX", "CH1", "DL10"}, + {"HDMI_CH4_MUX", "CH2", "DL10"}, + {"HDMI_CH4_MUX", "CH3", "DL10"}, + {"HDMI_CH4_MUX", "CH4", "DL10"}, + {"HDMI_CH4_MUX", "CH5", "DL10"}, + {"HDMI_CH4_MUX", "CH6", "DL10"}, + {"HDMI_CH4_MUX", "CH7", "DL10"}, + + {"HDMI_CH5_MUX", "CH0", "DL10"}, + {"HDMI_CH5_MUX", "CH1", "DL10"}, + {"HDMI_CH5_MUX", "CH2", "DL10"}, + {"HDMI_CH5_MUX", "CH3", "DL10"}, + {"HDMI_CH5_MUX", "CH4", "DL10"}, + {"HDMI_CH5_MUX", "CH5", "DL10"}, + {"HDMI_CH5_MUX", "CH6", "DL10"}, + {"HDMI_CH5_MUX", "CH7", "DL10"}, + + {"HDMI_CH6_MUX", "CH0", "DL10"}, + {"HDMI_CH6_MUX", "CH1", "DL10"}, + {"HDMI_CH6_MUX", "CH2", "DL10"}, + {"HDMI_CH6_MUX", "CH3", "DL10"}, + {"HDMI_CH6_MUX", "CH4", "DL10"}, + {"HDMI_CH6_MUX", "CH5", "DL10"}, + {"HDMI_CH6_MUX", "CH6", "DL10"}, + {"HDMI_CH6_MUX", "CH7", "DL10"}, + + {"HDMI_CH7_MUX", "CH0", "DL10"}, + {"HDMI_CH7_MUX", "CH1", "DL10"}, + {"HDMI_CH7_MUX", "CH2", "DL10"}, + {"HDMI_CH7_MUX", "CH3", "DL10"}, + {"HDMI_CH7_MUX", "CH4", "DL10"}, + {"HDMI_CH7_MUX", "CH5", "DL10"}, + {"HDMI_CH7_MUX", "CH6", "DL10"}, + {"HDMI_CH7_MUX", "CH7", "DL10"}, + + {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"}, + {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"}, + + {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"}, + {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"}, + + {"ETDM3_OUT", NULL, "HDMI_OUT_MUX"}, + {"DPTX", NULL, "DPTX_OUT_MUX"}, + + {"ETDM_OUTPUT", NULL, "DPTX"}, + {"ETDM_OUTPUT", NULL, "ETDM1_OUT"}, + {"ETDM_OUTPUT", NULL, "ETDM2_OUT"}, + {"ETDM_OUTPUT", NULL, "ETDM3_OUT"}, + {"ETDM1_IN", NULL, "ETDM_INPUT"}, + {"ETDM2_IN", NULL, "ETDM_INPUT"}, +}; + +static int mt8188_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + unsigned long flags; + int ret = 0; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt); + spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); + etdm_data->en_ref_cnt++; + if (etdm_data->en_ref_cnt == 1) { + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + goto out; + + regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_EN); + } + +out: + spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); + return ret; +} + +static int mt8188_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + unsigned long flags; + int ret = 0; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt); + spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); + if (etdm_data->en_ref_cnt > 0) { + etdm_data->en_ref_cnt--; + if (etdm_data->en_ref_cnt == 0) { + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + goto out; + regmap_clear_bits(afe->regmap, etdm_reg.con0, + ETDM_CON0_EN); + } + } + +out: + spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); + return ret; +} + +static int etdm_cowork_slv_sel(int id, int slave_mode) +{ + if (slave_mode) { + switch (id) { + case MT8188_AFE_IO_ETDM1_IN: + return COWORK_ETDM_IN1_S; + case MT8188_AFE_IO_ETDM2_IN: + return COWORK_ETDM_IN2_S; + case MT8188_AFE_IO_ETDM1_OUT: + return COWORK_ETDM_OUT1_S; + case MT8188_AFE_IO_ETDM2_OUT: + return COWORK_ETDM_OUT2_S; + case MT8188_AFE_IO_ETDM3_OUT: + return COWORK_ETDM_OUT3_S; + default: + return -EINVAL; + } + } else { + switch (id) { + case MT8188_AFE_IO_ETDM1_IN: + return COWORK_ETDM_IN1_M; + case MT8188_AFE_IO_ETDM2_IN: + return COWORK_ETDM_IN2_M; + case MT8188_AFE_IO_ETDM1_OUT: + return COWORK_ETDM_OUT1_M; + case MT8188_AFE_IO_ETDM2_OUT: + return COWORK_ETDM_OUT2_M; + case MT8188_AFE_IO_ETDM3_OUT: + return COWORK_ETDM_OUT3_M; + default: + return -EINVAL; + } + } +} + +static int etdm_cowork_sync_sel(int id) +{ + switch (id) { + case MT8188_AFE_IO_ETDM1_IN: + return ETDM_SYNC_FROM_IN1; + case MT8188_AFE_IO_ETDM2_IN: + return ETDM_SYNC_FROM_IN2; + case MT8188_AFE_IO_ETDM1_OUT: + return ETDM_SYNC_FROM_OUT1; + case MT8188_AFE_IO_ETDM2_OUT: + return ETDM_SYNC_FROM_OUT2; + case MT8188_AFE_IO_ETDM3_OUT: + return ETDM_SYNC_FROM_OUT3; + default: + return -EINVAL; + } +} + +static int mt8188_etdm_sync_mode_slv(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + unsigned int reg = 0; + unsigned int mask; + unsigned int val; + int cowork_source_sel; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + cowork_source_sel = etdm_cowork_slv_sel(etdm_data->cowork_source_id, + true); + if (cowork_source_sel < 0) + return cowork_source_sel; + + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + reg = ETDM_COWORK_CON1; + mask = ETDM_IN1_SLAVE_SEL_MASK; + val = FIELD_PREP(ETDM_IN1_SLAVE_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM2_IN: + reg = ETDM_COWORK_CON2; + mask = ETDM_IN2_SLAVE_SEL_MASK; + val = FIELD_PREP(ETDM_IN2_SLAVE_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM1_OUT: + reg = ETDM_COWORK_CON0; + mask = ETDM_OUT1_SLAVE_SEL_MASK; + val = FIELD_PREP(ETDM_OUT1_SLAVE_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM2_OUT: + reg = ETDM_COWORK_CON2; + mask = ETDM_OUT2_SLAVE_SEL_MASK; + val = FIELD_PREP(ETDM_OUT2_SLAVE_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM3_OUT: + reg = ETDM_COWORK_CON2; + mask = ETDM_OUT3_SLAVE_SEL_MASK; + val = FIELD_PREP(ETDM_OUT3_SLAVE_SEL_MASK, cowork_source_sel); + break; + default: + return 0; + } + + regmap_update_bits(afe->regmap, reg, mask, val); + + return 0; +} + +static int mt8188_etdm_sync_mode_mst(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + unsigned int reg = 0; + unsigned int mask; + unsigned int val; + int cowork_source_sel; + int ret; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + cowork_source_sel = etdm_cowork_sync_sel(etdm_data->cowork_source_id); + if (cowork_source_sel < 0) + return cowork_source_sel; + + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + reg = ETDM_COWORK_CON1; + mask = ETDM_IN1_SYNC_SEL_MASK; + val = FIELD_PREP(ETDM_IN1_SYNC_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM2_IN: + reg = ETDM_COWORK_CON2; + mask = ETDM_IN2_SYNC_SEL_MASK; + val = FIELD_PREP(ETDM_IN2_SYNC_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM1_OUT: + reg = ETDM_COWORK_CON0; + mask = ETDM_OUT1_SYNC_SEL_MASK; + val = FIELD_PREP(ETDM_OUT1_SYNC_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM2_OUT: + reg = ETDM_COWORK_CON2; + mask = ETDM_OUT2_SYNC_SEL_MASK; + val = FIELD_PREP(ETDM_OUT2_SYNC_SEL_MASK, cowork_source_sel); + break; + case MT8188_AFE_IO_ETDM3_OUT: + reg = ETDM_COWORK_CON2; + mask = ETDM_OUT3_SYNC_SEL_MASK; + val = FIELD_PREP(ETDM_OUT3_SYNC_SEL_MASK, cowork_source_sel); + break; + default: + return 0; + } + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + regmap_update_bits(afe->regmap, reg, mask, val); + + regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_SYNC_MODE); + + return 0; +} + +static int mt8188_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + if (etdm_data->cowork_source_id == COWORK_ETDM_NONE) + return 0; + + if (etdm_data->slave_mode) + mt8188_etdm_sync_mode_slv(afe, dai_id); + else + mt8188_etdm_sync_mode_mst(afe, dai_id); + + return 0; +} + +/* dai ops */ +static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *mst_etdm_data; + int mst_dai_id; + int slv_dai_id; + int cg_id; + int i; + + if (is_cowork_mode(dai)) { + mst_dai_id = get_etdm_cowork_master_id(dai); + if (!is_valid_etdm_dai(mst_dai_id)) + return -EINVAL; + mtk_dai_etdm_enable_mclk(afe, mst_dai_id); + + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id); + if (cg_id >= 0) + mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); + + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + + for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { + slv_dai_id = mst_etdm_data->cowork_slv_id[i]; + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id); + if (cg_id >= 0) + mt8188_afe_enable_clk(afe, + afe_priv->clk[cg_id]); + } + } else { + mtk_dai_etdm_enable_mclk(afe, dai->id); + + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); + if (cg_id >= 0) + mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); + } + + return 0; +} + +static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *mst_etdm_data; + int mst_dai_id; + int slv_dai_id; + int cg_id; + int ret; + int i; + + if (!is_valid_etdm_dai(dai->id)) + return; + mst_etdm_data = afe_priv->dai_priv[dai->id]; + + dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, + mst_etdm_data->is_prepared); + + if (mst_etdm_data->is_prepared) { + mst_etdm_data->is_prepared = false; + + if (is_cowork_mode(dai)) { + mst_dai_id = get_etdm_cowork_master_id(dai); + if (!is_valid_etdm_dai(mst_dai_id)) + return; + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + + ret = mt8188_afe_disable_etdm(afe, mst_dai_id); + if (ret) + dev_dbg(afe->dev, "%s disable %d failed\n", + __func__, mst_dai_id); + + for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { + slv_dai_id = mst_etdm_data->cowork_slv_id[i]; + ret = mt8188_afe_disable_etdm(afe, slv_dai_id); + if (ret) + dev_dbg(afe->dev, "%s disable %d failed\n", + __func__, slv_dai_id); + } + } else { + ret = mt8188_afe_disable_etdm(afe, dai->id); + if (ret) + dev_dbg(afe->dev, "%s disable %d failed\n", + __func__, dai->id); + } + } + + if (is_cowork_mode(dai)) { + mst_dai_id = get_etdm_cowork_master_id(dai); + if (!is_valid_etdm_dai(mst_dai_id)) + return; + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id); + if (cg_id >= 0) + mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); + + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { + slv_dai_id = mst_etdm_data->cowork_slv_id[i]; + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id); + if (cg_id >= 0) + mt8188_afe_disable_clk(afe, + afe_priv->clk[cg_id]); + } + mtk_dai_etdm_disable_mclk(afe, mst_dai_id); + } else { + cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); + if (cg_id >= 0) + mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); + + mtk_dai_etdm_disable_mclk(afe, dai->id); + } +} + +static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe, + int dai_id, unsigned int rate) +{ + unsigned int mode = 0; + unsigned int reg = 0; + unsigned int val = 0; + unsigned int mask = (ETDM_IN_AFIFO_MODE_MASK | ETDM_IN_USE_AFIFO); + + if (rate != 0) + mode = mt8188_afe_fs_timing(rate); + + switch (dai_id) { + case MT8188_AFE_IO_ETDM1_IN: + reg = ETDM_IN1_AFIFO_CON; + if (rate == 0) + mode = MT8188_ETDM_IN1_1X_EN; + break; + case MT8188_AFE_IO_ETDM2_IN: + reg = ETDM_IN2_AFIFO_CON; + if (rate == 0) + mode = MT8188_ETDM_IN2_1X_EN; + break; + default: + return -EINVAL; + } + + val = (mode | ETDM_IN_USE_AFIFO); + + regmap_update_bits(afe->regmap, reg, mask, val); + return 0; +} + +static int mtk_dai_etdm_in_configure(struct mtk_base_afe *afe, + unsigned int rate, + unsigned int channels, + int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + bool slave_mode; + unsigned int data_mode; + unsigned int lrck_width; + unsigned int val = 0; + unsigned int mask = 0; + int ret; + int i; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + slave_mode = etdm_data->slave_mode; + data_mode = etdm_data->data_mode; + lrck_width = etdm_data->lrck_width; + + dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n", + __func__, rate, channels, dai_id); + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + /* afifo */ + if (slave_mode) + mtk_dai_etdm_fifo_mode(afe, dai_id, 0); + else + mtk_dai_etdm_fifo_mode(afe, dai_id, rate); + + /* con1 */ + if (lrck_width > 0) { + mask |= (ETDM_IN_CON1_LRCK_AUTO_MODE | + ETDM_IN_CON1_LRCK_WIDTH_MASK); + val |= FIELD_PREP(ETDM_IN_CON1_LRCK_WIDTH_MASK, lrck_width - 1); + } + regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val); + + mask = 0; + val = 0; + + /* con2 */ + if (!slave_mode) { + mask |= ETDM_IN_CON2_UPDATE_GAP_MASK; + if (rate == 352800 || rate == 384000) + val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 4); + else + val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 3); + } + mask |= (ETDM_IN_CON2_MULTI_IP_2CH_MODE | + ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK); + if (data_mode == MTK_DAI_ETDM_DATA_MULTI_PIN) { + val |= ETDM_IN_CON2_MULTI_IP_2CH_MODE | + FIELD_PREP(ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK, channels - 1); + } + regmap_update_bits(afe->regmap, etdm_reg.con2, mask, val); + + mask = 0; + val = 0; + + /* con3 */ + mask |= ETDM_IN_CON3_DISABLE_OUT_MASK; + for (i = 0; i < channels; i += 2) { + if (etdm_data->in_disable_ch[i] && + etdm_data->in_disable_ch[i + 1]) + val |= ETDM_IN_CON3_DISABLE_OUT(i >> 1); + } + if (!slave_mode) { + mask |= ETDM_IN_CON3_FS_MASK; + val |= FIELD_PREP(ETDM_IN_CON3_FS_MASK, get_etdm_fs_timing(rate)); + } + regmap_update_bits(afe->regmap, etdm_reg.con3, mask, val); + + mask = 0; + val = 0; + + /* con4 */ + mask |= (ETDM_IN_CON4_MASTER_LRCK_INV | ETDM_IN_CON4_MASTER_BCK_INV | + ETDM_IN_CON4_SLAVE_LRCK_INV | ETDM_IN_CON4_SLAVE_BCK_INV); + if (slave_mode) { + if (etdm_data->lrck_inv) + val |= ETDM_IN_CON4_SLAVE_LRCK_INV; + if (etdm_data->bck_inv) + val |= ETDM_IN_CON4_SLAVE_BCK_INV; + } else { + if (etdm_data->lrck_inv) + val |= ETDM_IN_CON4_MASTER_LRCK_INV; + if (etdm_data->bck_inv) + val |= ETDM_IN_CON4_MASTER_BCK_INV; + } + regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val); + + mask = 0; + val = 0; + + /* con5 */ + mask |= ETDM_IN_CON5_LR_SWAP_MASK; + mask |= ETDM_IN_CON5_ENABLE_ODD_MASK; + for (i = 0; i < channels; i += 2) { + if (etdm_data->in_disable_ch[i] && + !etdm_data->in_disable_ch[i + 1]) { + val |= ETDM_IN_CON5_LR_SWAP(i >> 1); + val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1); + } else if (!etdm_data->in_disable_ch[i] && + etdm_data->in_disable_ch[i + 1]) { + val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1); + } + } + regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val); + return 0; +} + +static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe, + unsigned int rate, + unsigned int channels, + int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + bool slave_mode; + unsigned int lrck_width; + unsigned int val = 0; + unsigned int mask = 0; + int fs = 0; + int ret; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + slave_mode = etdm_data->slave_mode; + lrck_width = etdm_data->lrck_width; + + dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n", + __func__, rate, channels, dai_id); + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + /* con0 */ + mask = ETDM_OUT_CON0_RELATCH_DOMAIN_MASK; + val = FIELD_PREP(ETDM_OUT_CON0_RELATCH_DOMAIN_MASK, + ETDM_RELATCH_TIMING_A1A2SYS); + regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val); + + mask = 0; + val = 0; + + /* con1 */ + if (lrck_width > 0) { + mask |= (ETDM_OUT_CON1_LRCK_AUTO_MODE | + ETDM_OUT_CON1_LRCK_WIDTH_MASK); + val |= FIELD_PREP(ETDM_OUT_CON1_LRCK_WIDTH_MASK, lrck_width - 1); + } + regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val); + + mask = 0; + val = 0; + + if (!slave_mode) { + /* con4 */ + mask |= ETDM_OUT_CON4_FS_MASK; + val |= FIELD_PREP(ETDM_OUT_CON4_FS_MASK, get_etdm_fs_timing(rate)); + } + + mask |= ETDM_OUT_CON4_RELATCH_EN_MASK; + if (dai_id == MT8188_AFE_IO_ETDM1_OUT) + fs = MT8188_ETDM_OUT1_1X_EN; + else if (dai_id == MT8188_AFE_IO_ETDM2_OUT) + fs = MT8188_ETDM_OUT2_1X_EN; + + val |= FIELD_PREP(ETDM_OUT_CON4_RELATCH_EN_MASK, fs); + + regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val); + + mask = 0; + val = 0; + + /* con5 */ + mask |= (ETDM_OUT_CON5_MASTER_LRCK_INV | ETDM_OUT_CON5_MASTER_BCK_INV | + ETDM_OUT_CON5_SLAVE_LRCK_INV | ETDM_OUT_CON5_SLAVE_BCK_INV); + if (slave_mode) { + if (etdm_data->lrck_inv) + val |= ETDM_OUT_CON5_SLAVE_LRCK_INV; + if (etdm_data->bck_inv) + val |= ETDM_OUT_CON5_SLAVE_BCK_INV; + } else { + if (etdm_data->lrck_inv) + val |= ETDM_OUT_CON5_MASTER_LRCK_INV; + if (etdm_data->bck_inv) + val |= ETDM_OUT_CON5_MASTER_BCK_INV; + } + regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val); + + return 0; +} + +static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id); + int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id); + int apll_clk_id; + int apll; + int ret; + + if (clk_id < 0 || clkdiv_id < 0) + return -EINVAL; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT) + regmap_set_bits(afe->regmap, etdm_reg.con1, + ETDM_CON1_MCLK_OUTPUT); + else + regmap_clear_bits(afe->regmap, etdm_reg.con1, + ETDM_CON1_MCLK_OUTPUT); + + if (etdm_data->mclk_freq) { + apll = etdm_data->mclk_apll; + apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll); + if (apll_clk_id < 0) + return apll_clk_id; + + /* select apll */ + ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clk_id], + afe_priv->clk[apll_clk_id]); + if (ret) + return ret; + + /* set rate */ + ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id], + etdm_data->mclk_freq); + if (ret) + return ret; + } else { + if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT) + dev_dbg(afe->dev, "%s mclk freq = 0\n", __func__); + } + + return 0; +} + +static int mtk_dai_etdm_configure(struct mtk_base_afe *afe, + unsigned int rate, + unsigned int channels, + unsigned int bit_width, + int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct etdm_con_reg etdm_reg; + bool slave_mode; + unsigned int etdm_channels; + unsigned int val = 0; + unsigned int mask = 0; + unsigned int bck; + unsigned int wlen = get_etdm_wlen(bit_width); + int ret; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + slave_mode = etdm_data->slave_mode; + + ret = get_etdm_reg(dai_id, &etdm_reg); + if (ret < 0) + return ret; + + dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, clock %u slv %u\n", + __func__, etdm_data->format, etdm_data->data_mode, + etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv, + etdm_data->clock_mode, etdm_data->slave_mode); + dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n", + __func__, rate, channels, bit_width, dai_id); + + etdm_channels = (etdm_data->data_mode == MTK_DAI_ETDM_DATA_ONE_PIN) ? + get_etdm_ch_fixup(channels) : 2; + + bck = rate * etdm_channels * wlen; + if (bck > MT8188_ETDM_NORMAL_MAX_BCK_RATE) { + dev_err(afe->dev, "%s bck rate %u not support\n", + __func__, bck); + return -EINVAL; + } + + /* con0 */ + mask |= ETDM_CON0_BIT_LEN_MASK; + val |= FIELD_PREP(ETDM_CON0_BIT_LEN_MASK, bit_width - 1); + mask |= ETDM_CON0_WORD_LEN_MASK; + val |= FIELD_PREP(ETDM_CON0_WORD_LEN_MASK, wlen - 1); + mask |= ETDM_CON0_FORMAT_MASK; + val |= FIELD_PREP(ETDM_CON0_FORMAT_MASK, etdm_data->format); + mask |= ETDM_CON0_CH_NUM_MASK; + val |= FIELD_PREP(ETDM_CON0_CH_NUM_MASK, etdm_channels - 1); + + mask |= ETDM_CON0_SLAVE_MODE; + if (slave_mode) { + if (dai_id == MT8188_AFE_IO_ETDM1_OUT) { + dev_err(afe->dev, "%s id %d only support master mode\n", + __func__, dai_id); + return -EINVAL; + } + val |= ETDM_CON0_SLAVE_MODE; + } + regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val); + + if (get_etdm_dir(dai_id) == ETDM_IN) + mtk_dai_etdm_in_configure(afe, rate, channels, dai_id); + else + mtk_dai_etdm_out_configure(afe, rate, channels, dai_id); + + return 0; +} + +static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + unsigned int rate = params_rate(params); + unsigned int bit_width = params_width(params); + unsigned int channels = params_channels(params); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *mst_etdm_data; + int mst_dai_id; + int slv_dai_id; + int ret; + int i; + + dev_dbg(afe->dev, "%s '%s' period %u-%u\n", + __func__, snd_pcm_stream_str(substream), + params_period_size(params), params_periods(params)); + + if (is_cowork_mode(dai)) { + mst_dai_id = get_etdm_cowork_master_id(dai); + if (!is_valid_etdm_dai(mst_dai_id)) + return -EINVAL; + + ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id); + if (ret) + return ret; + + ret = mtk_dai_etdm_configure(afe, rate, channels, + bit_width, mst_dai_id); + if (ret) + return ret; + + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { + slv_dai_id = mst_etdm_data->cowork_slv_id[i]; + ret = mtk_dai_etdm_configure(afe, rate, channels, + bit_width, slv_dai_id); + if (ret) + return ret; + + ret = mt8188_etdm_sync_mode_configure(afe, slv_dai_id); + if (ret) + return ret; + } + } else { + ret = mtk_dai_etdm_mclk_configure(afe, dai->id); + if (ret) + return ret; + + ret = mtk_dai_etdm_configure(afe, rate, channels, + bit_width, dai->id); + if (ret) + return ret; + } + + return 0; +} + +static int mtk_dai_etdm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *mst_etdm_data; + int mst_dai_id; + int slv_dai_id; + int ret; + int i; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + mst_etdm_data = afe_priv->dai_priv[dai->id]; + + dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, + mst_etdm_data->is_prepared); + + if (mst_etdm_data->is_prepared) + return 0; + + mst_etdm_data->is_prepared = true; + + if (is_cowork_mode(dai)) { + mst_dai_id = get_etdm_cowork_master_id(dai); + if (!is_valid_etdm_dai(mst_dai_id)) + return -EINVAL; + mst_etdm_data = afe_priv->dai_priv[mst_dai_id]; + + for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) { + slv_dai_id = mst_etdm_data->cowork_slv_id[i]; + ret = mt8188_afe_enable_etdm(afe, slv_dai_id); + if (ret) { + dev_dbg(afe->dev, "%s enable %d failed\n", + __func__, slv_dai_id); + + return ret; + } + } + + ret = mt8188_afe_enable_etdm(afe, mst_dai_id); + if (ret) { + dev_dbg(afe->dev, "%s enable %d failed\n", + __func__, mst_dai_id); + + return ret; + } + } else { + ret = mt8188_afe_enable_etdm(afe, dai->id); + if (ret) { + dev_dbg(afe->dev, "%s enable %d failed\n", + __func__, dai->id); + + return ret; + } + } + + return 0; +} + +static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + int apll_rate; + int apll; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + + if (freq == 0) { + etdm_data->mclk_freq = freq; + return 0; + } + + if (etdm_data->mclk_fixed_apll == 0) + apll = mt8188_afe_get_default_mclk_source_by_rate(freq); + else + apll = etdm_data->mclk_apll; + + apll_rate = mt8188_afe_get_mclk_source_rate(afe, apll); + + if (freq > apll_rate) { + dev_err(afe->dev, "freq %d > apll rate %d\n", freq, apll_rate); + return -EINVAL; + } + + if (apll_rate % freq != 0) { + dev_err(afe->dev, "APLL%d cannot generate freq Hz\n", apll); + return -EINVAL; + } + + if (etdm_data->mclk_fixed_apll == 0) + etdm_data->mclk_apll = apll; + etdm_data->mclk_freq = freq; + + return 0; +} + +static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + int dai_id; + + dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n", + __func__, dai->id, freq, dir); + if (is_cowork_mode(dai)) + dai_id = get_etdm_cowork_master_id(dai); + else + dai_id = dai->id; + + if (!is_valid_etdm_dai(dai_id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai_id]; + etdm_data->mclk_dir = dir; + return mtk_dai_etdm_cal_mclk(afe, freq, dai_id); +} + +static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + dev_dbg(dai->dev, "%s id %d slot_width %d\n", + __func__, dai->id, slot_width); + + etdm_data->slots = slots; + etdm_data->lrck_width = slot_width; + return 0; +} + +static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + etdm_data->format = MTK_DAI_ETDM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_RIGHT_J: + etdm_data->format = MTK_DAI_ETDM_FORMAT_RJ; + break; + case SND_SOC_DAIFMT_DSP_A: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA; + break; + case SND_SOC_DAIFMT_DSP_B: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_NB_IF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = true; + break; + case SND_SOC_DAIFMT_IB_NF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_IB_IF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + etdm_data->slave_mode = true; + break; + case SND_SOC_DAIFMT_BP_FP: + etdm_data->slave_mode = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_dai_hdmitx_dptx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); + + if (cg_id >= 0) + mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]); + + mtk_dai_etdm_enable_mclk(afe, dai->id); + + return 0; +} + +static void mtk_dai_hdmitx_dptx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id); + struct mtk_dai_etdm_priv *etdm_data; + int ret; + + if (!is_valid_etdm_dai(dai->id)) + return; + etdm_data = afe_priv->dai_priv[dai->id]; + + if (etdm_data->is_prepared) { + etdm_data->is_prepared = false; + /* disable etdm_out3 */ + ret = mt8188_afe_disable_etdm(afe, dai->id); + if (ret) + dev_dbg(afe->dev, "%s disable failed\n", __func__); + + /* disable dptx interface */ + if (dai->id == MT8188_AFE_IO_DPTX) + regmap_clear_bits(afe->regmap, AFE_DPTX_CON, + AFE_DPTX_CON_ON); + } + + mtk_dai_etdm_disable_mclk(afe, dai->id); + + if (cg_id >= 0) + mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]); +} + +static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel) +{ + switch (channel) { + case 1 ... 2: + return AFE_DPTX_CON_CH_EN_2CH; + case 3 ... 4: + return AFE_DPTX_CON_CH_EN_4CH; + case 5 ... 6: + return AFE_DPTX_CON_CH_EN_6CH; + case 7 ... 8: + return AFE_DPTX_CON_CH_EN_8CH; + default: + return AFE_DPTX_CON_CH_EN_2CH; + } +} + +static unsigned int mtk_dai_get_dptx_ch(unsigned int ch) +{ + return (ch > 2) ? + AFE_DPTX_CON_CH_NUM_8CH : AFE_DPTX_CON_CH_NUM_2CH; +} + +static unsigned int mtk_dai_get_dptx_wlen(snd_pcm_format_t format) +{ + return snd_pcm_format_physical_width(format) <= 16 ? + AFE_DPTX_CON_16BIT : AFE_DPTX_CON_24BIT; +} + +static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + snd_pcm_format_t format = params_format(params); + int width = snd_pcm_format_physical_width(format); + int ret; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + /* dptx configure */ + if (dai->id == MT8188_AFE_IO_DPTX) { + regmap_update_bits(afe->regmap, AFE_DPTX_CON, + AFE_DPTX_CON_CH_EN_MASK, + mtk_dai_get_dptx_ch_en(channels)); + regmap_update_bits(afe->regmap, AFE_DPTX_CON, + AFE_DPTX_CON_CH_NUM_MASK, + mtk_dai_get_dptx_ch(channels)); + regmap_update_bits(afe->regmap, AFE_DPTX_CON, + AFE_DPTX_CON_16BIT_MASK, + mtk_dai_get_dptx_wlen(format)); + + if (mtk_dai_get_dptx_ch(channels) == AFE_DPTX_CON_CH_NUM_8CH) { + etdm_data->data_mode = MTK_DAI_ETDM_DATA_ONE_PIN; + channels = 8; + } else { + channels = 2; + } + } else { + etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN; + } + + ret = mtk_dai_etdm_mclk_configure(afe, dai->id); + if (ret) + return ret; + + ret = mtk_dai_etdm_configure(afe, rate, channels, width, dai->id); + + return ret; +} + +static int mtk_dai_hdmitx_dptx_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id, + etdm_data->is_prepared); + + if (etdm_data->is_prepared) + return 0; + + etdm_data->is_prepared = true; + + /* enable dptx interface */ + if (dai->id == MT8188_AFE_IO_DPTX) + regmap_set_bits(afe->regmap, AFE_DPTX_CON, AFE_DPTX_CON_ON); + + /* enable etdm_out3 */ + return mt8188_afe_enable_etdm(afe, dai->id); +} + +static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai, + int clk_id, + unsigned int freq, + int dir) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + + if (!is_valid_etdm_dai(dai->id)) + return -EINVAL; + etdm_data = afe_priv->dai_priv[dai->id]; + + dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n", + __func__, dai->id, freq, dir); + + etdm_data->mclk_dir = dir; + return mtk_dai_etdm_cal_mclk(afe, freq, dai->id); +} + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .prepare = mtk_dai_etdm_prepare, + .set_sysclk = mtk_dai_etdm_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, + .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { + .startup = mtk_dai_hdmitx_dptx_startup, + .shutdown = mtk_dai_hdmitx_dptx_shutdown, + .hw_params = mtk_dai_hdmitx_dptx_hw_params, + .prepare = mtk_dai_hdmitx_dptx_prepare, + .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +/* dai driver */ +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_192000) + +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { + { + .name = "DPTX", + .id = MT8188_AFE_IO_DPTX, + .playback = { + .stream_name = "DPTX", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_hdmitx_dptx_ops, + }, + { + .name = "ETDM1_IN", + .id = MT8188_AFE_IO_ETDM1_IN, + .capture = { + .stream_name = "ETDM1_IN", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + }, + { + .name = "ETDM2_IN", + .id = MT8188_AFE_IO_ETDM2_IN, + .capture = { + .stream_name = "ETDM2_IN", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + }, + { + .name = "ETDM1_OUT", + .id = MT8188_AFE_IO_ETDM1_OUT, + .playback = { + .stream_name = "ETDM1_OUT", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + }, + { + .name = "ETDM2_OUT", + .id = MT8188_AFE_IO_ETDM2_OUT, + .playback = { + .stream_name = "ETDM2_OUT", + .channels_min = 1, + .channels_max = 16, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + }, + { + .name = "ETDM3_OUT", + .id = MT8188_AFE_IO_ETDM3_OUT, + .playback = { + .stream_name = "ETDM3_OUT", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_hdmitx_dptx_ops, + }, +}; + +static void mt8188_etdm_update_sync_info(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + struct mtk_dai_etdm_priv *mst_data; + int mst_dai_id; + int i; + + for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) { + etdm_data = afe_priv->dai_priv[i]; + if (etdm_data->cowork_source_id != COWORK_ETDM_NONE) { + mst_dai_id = etdm_data->cowork_source_id; + mst_data = afe_priv->dai_priv[mst_dai_id]; + if (mst_data->cowork_source_id != COWORK_ETDM_NONE) + dev_err(afe->dev, "%s [%d] wrong sync source\n", + __func__, i); + mst_data->cowork_slv_id[mst_data->cowork_slv_count] = i; + mst_data->cowork_slv_count++; + } + } +} + +static void mt8188_dai_etdm_parse_of(struct mtk_base_afe *afe) +{ + const struct device_node *of_node = afe->dev->of_node; + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + char prop[48]; + u8 disable_chn[MT8188_ETDM_MAX_CHANNELS]; + int max_chn = MT8188_ETDM_MAX_CHANNELS; + unsigned int sync_id; + u32 sel; + int ret; + int dai_id; + int i, j; + struct { + const char *name; + const unsigned int sync_id; + } of_afe_etdms[MT8188_AFE_IO_ETDM_NUM] = { + {"etdm-in1", ETDM_SYNC_FROM_IN1}, + {"etdm-in2", ETDM_SYNC_FROM_IN2}, + {"etdm-out1", ETDM_SYNC_FROM_OUT1}, + {"etdm-out2", ETDM_SYNC_FROM_OUT2}, + {"etdm-out3", ETDM_SYNC_FROM_OUT3}, + }; + + for (i = 0; i < MT8188_AFE_IO_ETDM_NUM; i++) { + dai_id = ETDM_TO_DAI_ID(i); + etdm_data = afe_priv->dai_priv[dai_id]; + + snprintf(prop, sizeof(prop), "mediatek,%s-multi-pin-mode", + of_afe_etdms[i].name); + + etdm_data->data_mode = of_property_read_bool(of_node, prop); + + snprintf(prop, sizeof(prop), "mediatek,%s-cowork-source", + of_afe_etdms[i].name); + + ret = of_property_read_u32(of_node, prop, &sel); + if (ret == 0) { + if (sel >= MT8188_AFE_IO_ETDM_NUM) { + dev_err(afe->dev, "%s invalid id=%d\n", + __func__, sel); + etdm_data->cowork_source_id = COWORK_ETDM_NONE; + } else { + sync_id = of_afe_etdms[sel].sync_id; + etdm_data->cowork_source_id = + sync_to_dai_id(sync_id); + } + } else { + etdm_data->cowork_source_id = COWORK_ETDM_NONE; + } + } + + /* etdm in only */ + for (i = 0; i < 2; i++) { + snprintf(prop, sizeof(prop), "mediatek,%s-chn-disabled", + of_afe_etdms[i].name); + + ret = of_property_read_variable_u8_array(of_node, prop, + disable_chn, + 1, max_chn); + if (ret < 0) + continue; + + for (j = 0; j < ret; j++) { + if (disable_chn[j] >= MT8188_ETDM_MAX_CHANNELS) + dev_err(afe->dev, "%s [%d] invalid chn %u\n", + __func__, j, disable_chn[j]); + else + etdm_data->in_disable_ch[disable_chn[j]] = true; + } + } + mt8188_etdm_update_sync_info(afe); +} + +static int init_etdm_priv_data(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_priv; + int i; + + for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) { + etdm_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_dai_etdm_priv), + GFP_KERNEL); + if (!etdm_priv) + return -ENOMEM; + + afe_priv->dai_priv[i] = etdm_priv; + } + + afe_priv->dai_priv[MT8188_AFE_IO_DPTX] = + afe_priv->dai_priv[MT8188_AFE_IO_ETDM3_OUT]; + + mt8188_dai_etdm_parse_of(afe); + return 0; +} + +int mt8188_dai_etdm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_etdm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver); + + dai->dapm_widgets = mtk_dai_etdm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets); + dai->dapm_routes = mtk_dai_etdm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes); + dai->controls = mtk_dai_etdm_controls; + dai->num_controls = ARRAY_SIZE(mtk_dai_etdm_controls); + + return init_etdm_priv_data(afe); +} diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c new file mode 100644 index 000000000000..5bc854a8f3df --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI PCM I/F Control + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8188-afe-clk.h" +#include "mt8188-afe-common.h" +#include "mt8188-reg.h" + +enum { + MTK_DAI_PCM_FMT_I2S, + MTK_DAI_PCM_FMT_EIAJ, + MTK_DAI_PCM_FMT_MODEA, + MTK_DAI_PCM_FMT_MODEB, +}; + +enum { + MTK_DAI_PCM_CLK_A1SYS, + MTK_DAI_PCM_CLK_A2SYS, + MTK_DAI_PCM_CLK_26M_48K, + MTK_DAI_PCM_CLK_26M_441K, +}; + +struct mtk_dai_pcm_rate { + unsigned int rate; + unsigned int reg_value; +}; + +struct mtk_dai_pcmif_priv { + unsigned int slave_mode; + unsigned int lrck_inv; + unsigned int bck_inv; + unsigned int format; +}; + +static const struct mtk_dai_pcm_rate mtk_dai_pcm_rates[] = { + { .rate = 8000, .reg_value = 0, }, + { .rate = 16000, .reg_value = 1, }, + { .rate = 32000, .reg_value = 2, }, + { .rate = 48000, .reg_value = 3, }, + { .rate = 11025, .reg_value = 1, }, + { .rate = 22050, .reg_value = 2, }, + { .rate = 44100, .reg_value = 3, }, +}; + +static int mtk_dai_pcm_mode(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_dai_pcm_rates); i++) + if (mtk_dai_pcm_rates[i].rate == rate) + return mtk_dai_pcm_rates[i].reg_value; + + return -EINVAL; +} + +static const struct snd_kcontrol_new mtk_dai_pcm_o000_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN0, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN0_2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_pcm_o001_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN1_2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { + SND_SOC_DAPM_MIXER("I002", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I003", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("O000", SND_SOC_NOPM, 0, 0, + mtk_dai_pcm_o000_mix, + ARRAY_SIZE(mtk_dai_pcm_o000_mix)), + SND_SOC_DAPM_MIXER("O001", SND_SOC_NOPM, 0, 0, + mtk_dai_pcm_o001_mix, + ARRAY_SIZE(mtk_dai_pcm_o001_mix)), + + SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, 0, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("PCM1_INPUT"), + SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"), + + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"), + SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"), +}; + +static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { + {"I002", NULL, "PCM1 Capture"}, + {"I003", NULL, "PCM1 Capture"}, + + {"O000", "I000 Switch", "I000"}, + {"O001", "I001 Switch", "I001"}, + + {"O000", "I070 Switch", "I070"}, + {"O001", "I071 Switch", "I071"}, + + {"PCM1 Playback", NULL, "O000"}, + {"PCM1 Playback", NULL, "O001"}, + + {"PCM1 Playback", NULL, "PCM_1_EN"}, + {"PCM1 Playback", NULL, "aud_asrc12"}, + {"PCM1 Playback", NULL, "aud_pcmif"}, + + {"PCM1 Capture", NULL, "PCM_1_EN"}, + {"PCM1 Capture", NULL, "aud_asrc11"}, + {"PCM1 Capture", NULL, "aud_pcmif"}, + + {"PCM1_OUTPUT", NULL, "PCM1 Playback"}, + {"PCM1 Capture", NULL, "PCM1_INPUT"}, +}; + +static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv = NULL; + unsigned int slave_mode; + unsigned int lrck_inv; + unsigned int bck_inv; + unsigned int fmt; + unsigned int bit_width = dai->sample_bits; + unsigned int val = 0; + unsigned int mask = 0; + int fs = 0; + int mode = 0; + + if (dai->id < 0) + return -EINVAL; + + pcmif_priv = afe_priv->dai_priv[dai->id]; + slave_mode = pcmif_priv->slave_mode; + lrck_inv = pcmif_priv->lrck_inv; + bck_inv = pcmif_priv->bck_inv; + fmt = pcmif_priv->format; + + /* sync freq mode */ + fs = mt8188_afe_fs_timing(runtime->rate); + if (fs < 0) + return -EINVAL; + + val |= FIELD_PREP(PCM_INTF_CON2_SYNC_FREQ_MODE_MASK, fs); + mask |= PCM_INTF_CON2_SYNC_FREQ_MODE_MASK; + + /* clk domain sel */ + if (runtime->rate % 8000) + val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK, + MTK_DAI_PCM_CLK_26M_441K); + else + val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK, + MTK_DAI_PCM_CLK_26M_48K); + mask |= PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK; + + regmap_update_bits(afe->regmap, PCM_INTF_CON2, mask, val); + + val = 0; + mask = 0; + + /* pcm mode */ + mode = mtk_dai_pcm_mode(runtime->rate); + if (mode < 0) + return -EINVAL; + + val |= FIELD_PREP(PCM_INTF_CON1_PCM_MODE_MASK, mode); + mask |= PCM_INTF_CON1_PCM_MODE_MASK; + + /* pcm format */ + val |= FIELD_PREP(PCM_INTF_CON1_PCM_FMT_MASK, fmt); + mask |= PCM_INTF_CON1_PCM_FMT_MASK; + + /* pcm sync length */ + if (fmt == MTK_DAI_PCM_FMT_MODEA || + fmt == MTK_DAI_PCM_FMT_MODEB) + val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, 1); + else + val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, bit_width); + mask |= PCM_INTF_CON1_SYNC_LENGTH_MASK; + + /* pcm bits, word length */ + if (bit_width > 16) { + val |= PCM_INTF_CON1_PCM_24BIT; + val |= PCM_INTF_CON1_PCM_WLEN_64BCK; + } else { + val |= PCM_INTF_CON1_PCM_16BIT; + val |= PCM_INTF_CON1_PCM_WLEN_32BCK; + } + mask |= PCM_INTF_CON1_PCM_BIT_MASK; + mask |= PCM_INTF_CON1_PCM_WLEN_MASK; + + /* master/slave */ + if (!slave_mode) { + val |= PCM_INTF_CON1_PCM_MASTER; + + if (lrck_inv) + val |= PCM_INTF_CON1_SYNC_OUT_INV; + if (bck_inv) + val |= PCM_INTF_CON1_BCLK_OUT_INV; + mask |= PCM_INTF_CON1_CLK_OUT_INV_MASK; + } else { + val |= PCM_INTF_CON1_PCM_SLAVE; + + if (lrck_inv) + val |= PCM_INTF_CON1_SYNC_IN_INV; + if (bck_inv) + val |= PCM_INTF_CON1_BCLK_IN_INV; + mask |= PCM_INTF_CON1_CLK_IN_INV_MASK; + + // TODO: add asrc setting for slave mode + } + mask |= PCM_INTF_CON1_PCM_M_S_MASK; + + regmap_update_bits(afe->regmap, PCM_INTF_CON1, mask, val); + + return 0; +} + +/* dai ops */ +static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (snd_soc_dai_get_widget_playback(dai)->active || + snd_soc_dai_get_widget_capture(dai)->active) + return 0; + + return mtk_dai_pcm_configure(substream, dai); +} + +static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv = NULL; + + dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt); + + if (dai->id < 0) + return -EINVAL; + + pcmif_priv = afe_priv->dai_priv[dai->id]; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + pcmif_priv->format = MTK_DAI_PCM_FMT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + pcmif_priv->format = MTK_DAI_PCM_FMT_MODEA; + break; + case SND_SOC_DAIFMT_DSP_B: + pcmif_priv->format = MTK_DAI_PCM_FMT_MODEB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + pcmif_priv->bck_inv = 0; + pcmif_priv->lrck_inv = 0; + break; + case SND_SOC_DAIFMT_NB_IF: + pcmif_priv->bck_inv = 0; + pcmif_priv->lrck_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + pcmif_priv->bck_inv = 1; + pcmif_priv->lrck_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + pcmif_priv->bck_inv = 1; + pcmif_priv->lrck_inv = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + pcmif_priv->slave_mode = 1; + break; + case SND_SOC_DAIFMT_BP_FP: + pcmif_priv->slave_mode = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { + .prepare = mtk_dai_pcm_prepare, + .set_fmt = mtk_dai_pcm_set_fmt, +}; + +/* dai driver */ +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = { + { + .name = "PCM1", + .id = MT8188_AFE_IO_PCM, + .playback = { + .stream_name = "PCM1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, +}; + +static int init_pcmif_priv_data(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_pcmif_priv *pcmif_priv; + + pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_pcmif_priv), + GFP_KERNEL); + if (!pcmif_priv) + return -ENOMEM; + + afe_priv->dai_priv[MT8188_AFE_IO_PCM] = pcmif_priv; + return 0; +} + +int mt8188_dai_pcm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_pcm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + dai->dapm_widgets = mtk_dai_pcm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + dai->dapm_routes = mtk_dai_pcm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + + return init_pcmif_priv_data(afe); +} diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c new file mode 100644 index 000000000000..919d74ea1934 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -0,0 +1,785 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt8188-mt6359.c -- MT8188-MT6359 ALSA SoC machine driver + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Trevor Wu <trevor.wu@mediatek.com> + */ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "mt8188-afe-common.h" +#include "../../codecs/mt6359.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-soundcard-driver.h" + +/* FE */ +SND_SOC_DAILINK_DEFS(playback2, + DAILINK_COMP_ARRAY(COMP_CPU("DL2")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback3, + DAILINK_COMP_ARRAY(COMP_CPU("DL3")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback6, + DAILINK_COMP_ARRAY(COMP_CPU("DL6")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback7, + DAILINK_COMP_ARRAY(COMP_CPU("DL7")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback8, + DAILINK_COMP_ARRAY(COMP_CPU("DL8")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback10, + DAILINK_COMP_ARRAY(COMP_CPU("DL10")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(playback11, + DAILINK_COMP_ARRAY(COMP_CPU("DL11")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture1, + DAILINK_COMP_ARRAY(COMP_CPU("UL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture2, + DAILINK_COMP_ARRAY(COMP_CPU("UL2")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture3, + DAILINK_COMP_ARRAY(COMP_CPU("UL3")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture4, + DAILINK_COMP_ARRAY(COMP_CPU("UL4")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture5, + DAILINK_COMP_ARRAY(COMP_CPU("UL5")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture6, + DAILINK_COMP_ARRAY(COMP_CPU("UL6")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture8, + DAILINK_COMP_ARRAY(COMP_CPU("UL8")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture9, + DAILINK_COMP_ARRAY(COMP_CPU("UL9")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture10, + DAILINK_COMP_ARRAY(COMP_CPU("UL10")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +/* BE */ +SND_SOC_DAILINK_DEFS(adda, + DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), + DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", + "mt6359-snd-codec-aif1")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(dptx, + DAILINK_COMP_ARRAY(COMP_CPU("DPTX")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(etdm1_in, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(etdm2_in, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(etdm1_out, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(etdm2_out, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(etdm3_out, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(pcm1, + DAILINK_COMP_ARRAY(COMP_CPU("PCM1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +struct mt8188_mt6359_priv { + struct snd_soc_jack dp_jack; + struct snd_soc_jack hdmi_jack; +}; + +struct mt8188_card_data { + const char *name; + unsigned long quirk; +}; + +static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +#define CKSYS_AUD_TOP_CFG 0x032c +#define CKSYS_AUD_TOP_MON 0x0330 + +static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct snd_soc_component *cmpnt_codec = + asoc_rtd_to_codec(rtd, 0)->component; + struct mtk_base_afe *afe; + struct mt8188_afe_private *afe_priv; + struct mtkaif_param *param; + int chosen_phase_1, chosen_phase_2; + int prev_cycle_1, prev_cycle_2; + int test_done_1, test_done_2; + int cycle_1, cycle_2; + int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM]; + int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM]; + int mtkaif_calibration_num_phase; + bool mtkaif_calibration_ok; + unsigned int monitor = 0; + int counter; + int phase; + int i; + + if (!cmpnt_afe) + return -EINVAL; + + afe = snd_soc_component_get_drvdata(cmpnt_afe); + afe_priv = afe->platform_priv; + param = &afe_priv->mtkaif_params; + + dev_dbg(afe->dev, "%s(), start\n", __func__); + + param->mtkaif_calibration_ok = false; + for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++) { + param->mtkaif_chosen_phase[i] = -1; + param->mtkaif_phase_cycle[i] = 0; + mtkaif_chosen_phase[i] = -1; + mtkaif_phase_cycle[i] = 0; + } + + if (IS_ERR(afe_priv->topckgen)) { + dev_info(afe->dev, "%s() Cannot find topckgen controller\n", + __func__); + return 0; + } + + pm_runtime_get_sync(afe->dev); + mt6359_mtkaif_calibration_enable(cmpnt_codec); + + /* set test type to synchronizer pulse */ + regmap_update_bits(afe_priv->topckgen, + CKSYS_AUD_TOP_CFG, 0xffff, 0x4); + mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */ + mtkaif_calibration_ok = true; + + for (phase = 0; + phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok; + phase++) { + mt6359_set_mtkaif_calibration_phase(cmpnt_codec, + phase, phase, phase); + + regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1); + + test_done_1 = 0; + test_done_2 = 0; + + cycle_1 = -1; + cycle_2 = -1; + + counter = 0; + while (!(test_done_1 & test_done_2)) { + regmap_read(afe_priv->topckgen, + CKSYS_AUD_TOP_MON, &monitor); + test_done_1 = (monitor >> 28) & 0x1; + test_done_2 = (monitor >> 29) & 0x1; + + if (test_done_1 == 1) + cycle_1 = monitor & 0xf; + + if (test_done_2 == 1) + cycle_2 = (monitor >> 4) & 0xf; + + /* handle if never test done */ + if (++counter > 10000) { + dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n", + __func__, + cycle_1, cycle_2, monitor); + mtkaif_calibration_ok = false; + break; + } + } + + if (phase == 0) { + prev_cycle_1 = cycle_1; + prev_cycle_2 = cycle_2; + } + + if (cycle_1 != prev_cycle_1 && + mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) { + mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = phase - 1; + mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] = prev_cycle_1; + } + + if (cycle_2 != prev_cycle_2 && + mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) { + mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = phase - 1; + mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] = prev_cycle_2; + } + + regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1); + + if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] >= 0 && + mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] >= 0) + break; + } + + if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) { + mtkaif_calibration_ok = false; + chosen_phase_1 = 0; + } else { + chosen_phase_1 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0]; + } + + if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) { + mtkaif_calibration_ok = false; + chosen_phase_2 = 0; + } else { + chosen_phase_2 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1]; + } + + mt6359_set_mtkaif_calibration_phase(cmpnt_codec, + chosen_phase_1, + chosen_phase_2, + 0); + + mt6359_mtkaif_calibration_disable(cmpnt_codec); + pm_runtime_put(afe->dev); + + param->mtkaif_calibration_ok = mtkaif_calibration_ok; + param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = chosen_phase_1; + param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = chosen_phase_2; + + for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++) + param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i]; + + dev_info(afe->dev, "%s(), end, calibration ok %d\n", + __func__, param->mtkaif_calibration_ok); + + return 0; +} + +static int mt8188_mt6359_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *cmpnt_codec = + asoc_rtd_to_codec(rtd, 0)->component; + + /* set mtkaif protocol */ + mt6359_set_mtkaif_protocol(cmpnt_codec, + MT6359_MTKAIF_PROTOCOL_2_CLK_P2); + + /* mtkaif calibration */ + mt8188_mt6359_mtkaif_calibration(rtd); + + return 0; +} + +enum { + DAI_LINK_DL2_FE, + DAI_LINK_DL3_FE, + DAI_LINK_DL6_FE, + DAI_LINK_DL7_FE, + DAI_LINK_DL8_FE, + DAI_LINK_DL10_FE, + DAI_LINK_DL11_FE, + DAI_LINK_UL1_FE, + DAI_LINK_UL2_FE, + DAI_LINK_UL3_FE, + DAI_LINK_UL4_FE, + DAI_LINK_UL5_FE, + DAI_LINK_UL6_FE, + DAI_LINK_UL8_FE, + DAI_LINK_UL9_FE, + DAI_LINK_UL10_FE, + DAI_LINK_ADDA_BE, + DAI_LINK_DPTX_BE, + DAI_LINK_ETDM1_IN_BE, + DAI_LINK_ETDM2_IN_BE, + DAI_LINK_ETDM1_OUT_BE, + DAI_LINK_ETDM2_OUT_BE, + DAI_LINK_ETDM3_OUT_BE, + DAI_LINK_PCM1_BE, +}; + +static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int rate = params_rate(params); + unsigned int mclk_fs_ratio = 256; + unsigned int mclk_fs = rate * mclk_fs_ratio; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); + + return snd_soc_dai_set_sysclk(dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); +} + +static const struct snd_soc_ops mt8188_dptx_ops = { + .hw_params = mt8188_dptx_hw_params, +}; + +static int mt8188_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + /* fix BE i2s format to 32bit, clean param mask first */ + snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + + params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + +static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + int ret = 0; + + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + &priv->hdmi_jack); + if (ret) { + dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); + return ret; + } + + ret = snd_soc_component_set_jack(component, &priv->hdmi_jack, NULL); + if (ret) + dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", + __func__, component->name, ret); + + return ret; +} + +static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + int ret = 0; + + ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, + &priv->dp_jack); + if (ret) { + dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); + return ret; + } + + ret = snd_soc_component_set_jack(component, &priv->dp_jack, NULL); + if (ret) + dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n", + __func__, component->name, ret); + + return ret; +} + +static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { + /* FE */ + [DAI_LINK_DL2_FE] = { + .name = "DL2_FE", + .stream_name = "DL2 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback2), + }, + [DAI_LINK_DL3_FE] = { + .name = "DL3_FE", + .stream_name = "DL3 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback3), + }, + [DAI_LINK_DL6_FE] = { + .name = "DL6_FE", + .stream_name = "DL6 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback6), + }, + [DAI_LINK_DL7_FE] = { + .name = "DL7_FE", + .stream_name = "DL7 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback7), + }, + [DAI_LINK_DL8_FE] = { + .name = "DL8_FE", + .stream_name = "DL8 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback8), + }, + [DAI_LINK_DL10_FE] = { + .name = "DL10_FE", + .stream_name = "DL10 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback10), + }, + [DAI_LINK_DL11_FE] = { + .name = "DL11_FE", + .stream_name = "DL11 Playback", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback11), + }, + [DAI_LINK_UL1_FE] = { + .name = "UL1_FE", + .stream_name = "UL1 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture1), + }, + [DAI_LINK_UL2_FE] = { + .name = "UL2_FE", + .stream_name = "UL2 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture2), + }, + [DAI_LINK_UL3_FE] = { + .name = "UL3_FE", + .stream_name = "UL3 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture3), + }, + [DAI_LINK_UL4_FE] = { + .name = "UL4_FE", + .stream_name = "UL4 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture4), + }, + [DAI_LINK_UL5_FE] = { + .name = "UL5_FE", + .stream_name = "UL5 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture5), + }, + [DAI_LINK_UL6_FE] = { + .name = "UL6_FE", + .stream_name = "UL6 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture6), + }, + [DAI_LINK_UL8_FE] = { + .name = "UL8_FE", + .stream_name = "UL8 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture8), + }, + [DAI_LINK_UL9_FE] = { + .name = "UL9_FE", + .stream_name = "UL9 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture9), + }, + [DAI_LINK_UL10_FE] = { + .name = "UL10_FE", + .stream_name = "UL10 Capture", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST, + }, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture10), + }, + /* BE */ + [DAI_LINK_ADDA_BE] = { + .name = "ADDA_BE", + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = mt8188_mt6359_init, + SND_SOC_DAILINK_REG(adda), + }, + [DAI_LINK_DPTX_BE] = { + .name = "DPTX_BE", + .ops = &mt8188_dptx_ops, + .be_hw_params_fixup = mt8188_dptx_hw_params_fixup, + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(dptx), + }, + [DAI_LINK_ETDM1_IN_BE] = { + .name = "ETDM1_IN_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP, + .dpcm_capture = 1, + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(etdm1_in), + }, + [DAI_LINK_ETDM2_IN_BE] = { + .name = "ETDM2_IN_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(etdm2_in), + }, + [DAI_LINK_ETDM1_OUT_BE] = { + .name = "ETDM1_OUT_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(etdm1_out), + }, + [DAI_LINK_ETDM2_OUT_BE] = { + .name = "ETDM2_OUT_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(etdm2_out), + }, + [DAI_LINK_ETDM3_OUT_BE] = { + .name = "ETDM3_OUT_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(etdm3_out), + }, + [DAI_LINK_PCM1_BE] = { + .name = "PCM1_BE", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(pcm1), + }, +}; + +static struct snd_soc_card mt8188_mt6359_soc_card = { + .owner = THIS_MODULE, + .dai_link = mt8188_mt6359_dai_links, + .num_links = ARRAY_SIZE(mt8188_mt6359_dai_links), + .dapm_widgets = mt8188_mt6359_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets), + .controls = mt8188_mt6359_controls, + .num_controls = ARRAY_SIZE(mt8188_mt6359_controls), +}; + +static int mt8188_mt6359_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8188_mt6359_soc_card; + struct device_node *platform_node; + struct mt8188_mt6359_priv *priv; + struct mt8188_card_data *card_data; + struct snd_soc_dai_link *dai_link; + int ret, i; + + card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev); + card->dev = &pdev->dev; + + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) + return dev_err_probe(&pdev->dev, ret, "%s new card name parsing error\n", + __func__); + + if (!card->name) + card->name = card_data->name; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (of_property_read_bool(pdev->dev.of_node, "audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) + return ret; + } + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + ret = -EINVAL; + return dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n"); + } + + ret = parse_dai_link_info(card); + if (ret) + goto err; + + for_each_card_prelinks(card, i, dai_link) { + if (!dai_link->platforms->name) + dai_link->platforms->of_node = platform_node; + + if (strcmp(dai_link->name, "DPTX_BE") == 0) { + if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + dai_link->init = mt8188_dptx_codec_init; + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) + dai_link->init = mt8188_hdmi_codec_init; + } + } + + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", + __func__); +err: + of_node_put(platform_node); + clean_card_reference(card); + return ret; +} + +static struct mt8188_card_data mt8188_evb_card = { + .name = "mt8188_mt6359", +}; + +static const struct of_device_id mt8188_mt6359_dt_match[] = { + { + .compatible = "mediatek,mt8188-mt6359-evb", + .data = &mt8188_evb_card, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match); + +static struct platform_driver mt8188_mt6359_driver = { + .driver = { + .name = "mt8188_mt6359", + .of_match_table = mt8188_mt6359_dt_match, + .pm = &snd_soc_pm_ops, + }, + .probe = mt8188_mt6359_dev_probe, +}; + +module_platform_driver(mt8188_mt6359_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8188-MT6359 ALSA SoC machine driver"); +MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("mt8188 mt6359 soc card"); diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h new file mode 100644 index 000000000000..51cd1a83dd9d --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-reg.h @@ -0,0 +1,3180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8188-reg.h -- MediaTek 8188 audio driver reg definition + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Chun-Chia Chiu <chun-chia.chiu@mediatek.com> + */ + +#ifndef _MT8188_REG_H_ +#define _MT8188_REG_H_ + +#define AUDIO_TOP_CON0 (0x0000) +#define AUDIO_TOP_CON1 (0x0004) +#define AUDIO_TOP_CON2 (0x0008) +#define AUDIO_TOP_CON3 (0x000c) +#define AUDIO_TOP_CON4 (0x0010) +#define AUDIO_TOP_CON5 (0x0014) +#define AUDIO_TOP_CON6 (0x0018) +#define AFE_MAS_HADDR_MSB (0x0020) +#define AFE_MEMIF_ONE_HEART (0x0024) +#define AFE_MUX_SEL_CFG (0x0044) +#define PWR1_ASM_CON1 (0x0108) +#define ASYS_IRQ_CONFIG (0x0110) +#define ASYS_IRQ1_CON (0x0114) +#define ASYS_IRQ2_CON (0x0118) +#define ASYS_IRQ3_CON (0x011c) +#define ASYS_IRQ4_CON (0x0120) +#define ASYS_IRQ5_CON (0x0124) +#define ASYS_IRQ6_CON (0x0128) +#define ASYS_IRQ7_CON (0x012c) +#define ASYS_IRQ8_CON (0x0130) +#define ASYS_IRQ9_CON (0x0134) +#define ASYS_IRQ10_CON (0x0138) +#define ASYS_IRQ11_CON (0x013c) +#define ASYS_IRQ12_CON (0x0140) +#define ASYS_IRQ13_CON (0x0144) +#define ASYS_IRQ14_CON (0x0148) +#define ASYS_IRQ15_CON (0x014c) +#define ASYS_IRQ16_CON (0x0150) +#define ASYS_IRQ_CLR (0x0154) +#define ASYS_IRQ_STATUS (0x0158) +#define ASYS_IRQ_MON1 (0x015c) +#define ASYS_IRQ_MON2 (0x0160) +#define AFE_IRQ1_CON (0x0164) +#define AFE_IRQ2_CON (0x0168) +#define AFE_IRQ3_CON (0x016c) +#define AFE_IRQ_MCU_CLR (0x0170) +#define AFE_IRQ_STATUS (0x0174) +#define AFE_IRQ_MASK (0x0178) +#define ASYS_IRQ_MASK (0x017c) +#define AFE_IRQ3_CON_MON (0x01b0) +#define AFE_IRQ_MCU_MON2 (0x01b4) +#define AFE_IRQ8_CON (0x01b8) +#define AFE_IRQ9_CON (0x01bc) +#define AFE_IRQ10_CON (0x01c0) +#define AFE_IRQ9_CON_MON (0x01c4) +#define ADSP_IRQ_MASK (0x01c8) +#define ADSP_IRQ_STATUS (0x01cc) +#define AFE_SINEGEN_CON0 (0x01f0) +#define AFE_SINEGEN_CON1 (0x01f4) +#define AFE_SINEGEN_CON2 (0x01f8) +#define AFE_SINEGEN_CON3 (0x01fc) +#define AFE_SPDIF_OUT_CON0 (0x0380) +#define AFE_TDMOUT_CONN0 (0x0390) +#define PWR1_ASM_CON2 (0x03b0) +#define PWR1_ASM_CON3 (0x03b4) +#define AFE_APLL_TUNER_CFG (0x03f8) +#define AFE_APLL_TUNER_CFG1 (0x03fc) +#define AUDIO_TOP_STA0 (0x0400) +#define AUDIO_TOP_STA1 (0x0404) +#define AFE_GAIN1_CON0 (0x0410) +#define AFE_GAIN1_CON1 (0x0414) +#define AFE_GAIN1_CON2 (0x0418) +#define AFE_GAIN1_CON3 (0x041c) +#define AFE_GAIN1_CUR (0x0424) +#define AFE_GAIN2_CON0 (0x0428) +#define AFE_GAIN2_CON1 (0x042c) +#define AFE_GAIN2_CON2 (0x0430) +#define AFE_GAIN2_CON3 (0x0434) +#define AFE_GAIN2_CUR (0x043c) +#define AFE_IEC_CFG (0x0480) +#define AFE_IEC_NSNUM (0x0484) +#define AFE_IEC_BURST_INFO (0x0488) +#define AFE_IEC_BURST_LEN (0x048c) +#define AFE_IEC_NSADR (0x0490) +#define AFE_IEC_CHL_STAT0 (0x04a0) +#define AFE_IEC_CHL_STAT1 (0x04a4) +#define AFE_IEC_CHR_STAT0 (0x04a8) +#define AFE_IEC_CHR_STAT1 (0x04ac) +#define AFE_SPDIFIN_CFG0 (0x0500) +#define AFE_SPDIFIN_CFG1 (0x0504) +#define AFE_SPDIFIN_CHSTS1 (0x0508) +#define AFE_SPDIFIN_CHSTS2 (0x050c) +#define AFE_SPDIFIN_CHSTS3 (0x0510) +#define AFE_SPDIFIN_CHSTS4 (0x0514) +#define AFE_SPDIFIN_CHSTS5 (0x0518) +#define AFE_SPDIFIN_CHSTS6 (0x051c) +#define AFE_SPDIFIN_DEBUG1 (0x0520) +#define AFE_SPDIFIN_DEBUG2 (0x0524) +#define AFE_SPDIFIN_DEBUG3 (0x0528) +#define AFE_SPDIFIN_DEBUG4 (0x052c) +#define AFE_SPDIFIN_EC (0x0530) +#define AFE_SPDIFIN_CKLOCK_CFG (0x0534) +#define AFE_SPDIFIN_BR (0x053c) +#define AFE_SPDIFIN_BR_DBG1 (0x0540) +#define AFE_SPDIFIN_CKFBDIV (0x0544) +#define AFE_SPDIFIN_INT_EXT (0x0548) +#define AFE_SPDIFIN_INT_EXT2 (0x054c) +#define SPDIFIN_FREQ_INFO (0x0550) +#define SPDIFIN_FREQ_INFO_2 (0x0554) +#define SPDIFIN_FREQ_INFO_3 (0x0558) +#define SPDIFIN_FREQ_STATUS (0x055c) +#define SPDIFIN_USERCODE1 (0x0560) +#define SPDIFIN_USERCODE2 (0x0564) +#define SPDIFIN_USERCODE3 (0x0568) +#define SPDIFIN_USERCODE4 (0x056c) +#define SPDIFIN_USERCODE5 (0x0570) +#define SPDIFIN_USERCODE6 (0x0574) +#define SPDIFIN_USERCODE7 (0x0578) +#define SPDIFIN_USERCODE8 (0x057c) +#define SPDIFIN_USERCODE9 (0x0580) +#define SPDIFIN_USERCODE10 (0x0584) +#define SPDIFIN_USERCODE11 (0x0588) +#define SPDIFIN_USERCODE12 (0x058c) +#define AFE_SPDIFIN_APLL_TUNER_CFG (0x0594) +#define AFE_SPDIFIN_APLL_TUNER_CFG1 (0x0598) +#define ASYS_TOP_CON (0x0600) +#define AFE_LINEIN_APLL_TUNER_CFG (0x0610) +#define AFE_LINEIN_APLL_TUNER_MON (0x0614) +#define AFE_EARC_APLL_TUNER_CFG (0x0618) +#define AFE_EARC_APLL_TUNER_MON (0x061c) +#define PWR2_TOP_CON0 (0x0634) +#define PWR2_TOP_CON1 (0x0638) +#define PCM_INTF_CON1 (0x063c) +#define PCM_INTF_CON2 (0x0640) +#define AFE_CM0_CON (0x0660) +#define AFE_CM1_CON (0x0664) +#define AFE_CM2_CON (0x0668) +#define AFE_CM0_MON (0x0670) +#define AFE_CM1_MON (0x0674) +#define AFE_CM2_MON (0x0678) +#define AFE_MPHONE_MULTI_CON0 (0x06a4) +#define AFE_MPHONE_MULTI_CON1 (0x06a8) +#define AFE_MPHONE_MULTI_CON2 (0x06ac) +#define AFE_MPHONE_MULTI_MON (0x06b0) +#define AFE_MPHONE_MULTI_DET_REG_CON0 (0x06b4) +#define AFE_MPHONE_MULTI_DET_REG_CON1 (0x06b8) +#define AFE_MPHONE_MULTI_DET_REG_CON2 (0x06bc) +#define AFE_MPHONE_MULTI_DET_REG_CON3 (0x06c0) +#define AFE_MPHONE_MULTI_DET_MON0 (0x06c4) +#define AFE_MPHONE_MULTI_DET_MON1 (0x06c8) +#define AFE_MPHONE_MULTI_DET_MON2 (0x06d0) +#define AFE_MPHONE_MULTI2_CON0 (0x06d4) +#define AFE_MPHONE_MULTI2_CON1 (0x06d8) +#define AFE_MPHONE_MULTI2_CON2 (0x06dc) +#define AFE_MPHONE_MULTI2_MON (0x06e0) +#define AFE_MPHONE_MULTI2_DET_REG_CON0 (0x06e4) +#define AFE_MPHONE_MULTI2_DET_REG_CON1 (0x06e8) +#define AFE_MPHONE_MULTI2_DET_REG_CON2 (0x06ec) +#define AFE_MPHONE_MULTI2_DET_REG_CON3 (0x06f0) +#define AFE_MPHONE_MULTI2_DET_MON0 (0x06f4) +#define AFE_MPHONE_MULTI2_DET_MON1 (0x06f8) +#define AFE_MPHONE_MULTI2_DET_MON2 (0x06fc) +#define AFE_ADDA_IIR_COEF_02_01 (0x0700) +#define AFE_ADDA_IIR_COEF_04_03 (0x0704) +#define AFE_ADDA_IIR_COEF_06_05 (0x0708) +#define AFE_ADDA_IIR_COEF_08_07 (0x070c) +#define AFE_ADDA_IIR_COEF_10_09 (0x0710) +#define AFE_ADDA_ULCF_CFG_02_01 (0x0714) +#define AFE_ADDA_ULCF_CFG_04_03 (0x0718) +#define AFE_ADDA_ULCF_CFG_06_05 (0x071c) +#define AFE_ADDA_ULCF_CFG_08_07 (0x0720) +#define AFE_ADDA_ULCF_CFG_10_09 (0x0724) +#define AFE_ADDA_ULCF_CFG_12_11 (0x0728) +#define AFE_ADDA_ULCF_CFG_14_13 (0x072c) +#define AFE_ADDA_ULCF_CFG_16_15 (0x0730) +#define AFE_ADDA_ULCF_CFG_18_17 (0x0734) +#define AFE_ADDA_ULCF_CFG_20_19 (0x0738) +#define AFE_ADDA_ULCF_CFG_22_21 (0x073c) +#define AFE_ADDA_ULCF_CFG_24_23 (0x0740) +#define AFE_ADDA_ULCF_CFG_26_25 (0x0744) +#define AFE_ADDA_ULCF_CFG_28_27 (0x0748) +#define AFE_ADDA_ULCF_CFG_30_29 (0x074c) +#define AFE_ADDA6_IIR_COEF_02_01 (0x0750) +#define AFE_ADDA6_IIR_COEF_04_03 (0x0754) +#define AFE_ADDA6_IIR_COEF_06_05 (0x0758) +#define AFE_ADDA6_IIR_COEF_08_07 (0x075c) +#define AFE_ADDA6_IIR_COEF_10_09 (0x0760) +#define AFE_ADDA6_ULCF_CFG_02_01 (0x0764) +#define AFE_ADDA6_ULCF_CFG_04_03 (0x0768) +#define AFE_ADDA6_ULCF_CFG_06_05 (0x076c) +#define AFE_ADDA6_ULCF_CFG_08_07 (0x0770) +#define AFE_ADDA6_ULCF_CFG_10_09 (0x0774) +#define AFE_ADDA6_ULCF_CFG_12_11 (0x0778) +#define AFE_ADDA6_ULCF_CFG_14_13 (0x077c) +#define AFE_ADDA6_ULCF_CFG_16_15 (0x0780) +#define AFE_ADDA6_ULCF_CFG_18_17 (0x0784) +#define AFE_ADDA6_ULCF_CFG_20_19 (0x0788) +#define AFE_ADDA6_ULCF_CFG_22_21 (0x078c) +#define AFE_ADDA6_ULCF_CFG_24_23 (0x0790) +#define AFE_ADDA6_ULCF_CFG_26_25 (0x0794) +#define AFE_ADDA6_ULCF_CFG_28_27 (0x0798) +#define AFE_ADDA6_ULCF_CFG_30_29 (0x079c) +#define AFE_ADDA_MTKAIF_CFG0 (0x07a0) +#define AFE_ADDA_MTKAIF_SYNCWORD_CFG (0x07a8) +#define AFE_ADDA_MTKAIF_RX_CFG0 (0x07b4) +#define AFE_ADDA_MTKAIF_RX_CFG1 (0x07b8) +#define AFE_ADDA_MTKAIF_RX_CFG2 (0x07bc) +#define AFE_ADDA_MTKAIF_MON0 (0x07c8) +#define AFE_ADDA_MTKAIF_MON1 (0x07cc) +#define AFE_AUD_PAD_TOP (0x07d4) +#define AFE_ADDA6_MTKAIF_MON0 (0x07d8) +#define AFE_ADDA6_MTKAIF_MON1 (0x07dc) +#define AFE_ADDA6_MTKAIF_CFG0 (0x07e0) +#define AFE_ADDA6_MTKAIF_RX_CFG0 (0x07e4) +#define AFE_ADDA6_MTKAIF_RX_CFG1 (0x07e8) +#define AFE_ADDA6_MTKAIF_RX_CFG2 (0x07ec) +#define AFE_ADDA6_TOP_CON0 (0x07f0) +#define AFE_ADDA6_UL_SRC_CON0 (0x07f4) +#define AFE_ADDA6_UL_SRC_CON1 (0x07f8) +#define AFE_ADDA6_SRC_DEBUG (0x0800) +#define AFE_ADDA6_SRC_DEBUG_MON0 (0x0804) +#define AFE_ADDA6_UL_SRC_MON0 (0x0818) +#define AFE_ADDA6_UL_SRC_MON1 (0x081c) +#define AFE_CONN0_5 (0x0830) +#define AFE_CONN1_5 (0x0834) +#define AFE_CONN2_5 (0x0838) +#define AFE_CONN3_5 (0x083c) +#define AFE_CONN4_5 (0x0840) +#define AFE_CONN5_5 (0x0844) +#define AFE_CONN6_5 (0x0848) +#define AFE_CONN7_5 (0x084c) +#define AFE_CONN8_5 (0x0850) +#define AFE_CONN9_5 (0x0854) +#define AFE_CONN10_5 (0x0858) +#define AFE_CONN11_5 (0x085c) +#define AFE_CONN12_5 (0x0860) +#define AFE_CONN13_5 (0x0864) +#define AFE_CONN14_5 (0x0868) +#define AFE_CONN15_5 (0x086c) +#define AFE_CONN16_5 (0x0870) +#define AFE_CONN17_5 (0x0874) +#define AFE_CONN18_5 (0x0878) +#define AFE_CONN19_5 (0x087c) +#define AFE_CONN20_5 (0x0880) +#define AFE_CONN21_5 (0x0884) +#define AFE_CONN22_5 (0x0888) +#define AFE_CONN23_5 (0x088c) +#define AFE_CONN24_5 (0x0890) +#define AFE_CONN25_5 (0x0894) +#define AFE_CONN26_5 (0x0898) +#define AFE_CONN27_5 (0x089c) +#define AFE_CONN28_5 (0x08a0) +#define AFE_CONN29_5 (0x08a4) +#define AFE_CONN30_5 (0x08a8) +#define AFE_CONN31_5 (0x08ac) +#define AFE_CONN32_5 (0x08b0) +#define AFE_CONN33_5 (0x08b4) +#define AFE_CONN34_5 (0x08b8) +#define AFE_CONN35_5 (0x08bc) +#define AFE_CONN36_5 (0x08c0) +#define AFE_CONN37_5 (0x08c4) +#define AFE_CONN38_5 (0x08c8) +#define AFE_CONN39_5 (0x08cc) +#define AFE_CONN40_5 (0x08d0) +#define AFE_CONN41_5 (0x08d4) +#define AFE_CONN42_5 (0x08d8) +#define AFE_CONN43_5 (0x08dc) +#define AFE_CONN44_5 (0x08e0) +#define AFE_CONN45_5 (0x08e4) +#define AFE_CONN46_5 (0x08e8) +#define AFE_CONN47_5 (0x08ec) +#define AFE_CONN48_5 (0x08f0) +#define AFE_CONN49_5 (0x08f4) +#define AFE_CONN50_5 (0x08f8) +#define AFE_CONN51_5 (0x08fc) +#define AFE_CONN52_5 (0x0900) +#define AFE_CONN53_5 (0x0904) +#define AFE_CONN54_5 (0x0908) +#define AFE_CONN55_5 (0x090c) +#define AFE_CONN56_5 (0x0910) +#define AFE_CONN57_5 (0x0914) +#define AFE_CONN58_5 (0x0918) +#define AFE_CONN59_5 (0x091c) +#define AFE_CONN60_5 (0x0920) +#define AFE_CONN61_5 (0x0924) +#define AFE_CONN62_5 (0x0928) +#define AFE_CONN63_5 (0x092c) +#define AFE_CONN64_5 (0x0930) +#define AFE_CONN65_5 (0x0934) +#define AFE_CONN66_5 (0x0938) +#define AFE_CONN67_5 (0x093c) +#define AFE_CONN68_5 (0x0940) +#define AFE_CONN69_5 (0x0944) +#define AFE_CONN70_5 (0x0948) +#define AFE_CONN71_5 (0x094c) +#define AFE_CONN72_5 (0x0950) +#define AFE_CONN73_5 (0x0954) +#define AFE_CONN74_5 (0x0958) +#define AFE_CONN75_5 (0x095c) +#define AFE_CONN76_5 (0x0960) +#define AFE_CONN77_5 (0x0964) +#define AFE_CONN78_5 (0x0968) +#define AFE_CONN79_5 (0x096c) +#define AFE_CONN80_5 (0x0970) +#define AFE_CONN81_5 (0x0974) +#define AFE_CONN82_5 (0x0978) +#define AFE_CONN83_5 (0x097c) +#define AFE_CONN84_5 (0x0980) +#define AFE_CONN85_5 (0x0984) +#define AFE_CONN86_5 (0x0988) +#define AFE_CONN87_5 (0x098c) +#define AFE_CONN88_5 (0x0990) +#define AFE_CONN89_5 (0x0994) +#define AFE_CONN90_5 (0x0998) +#define AFE_CONN91_5 (0x099c) +#define AFE_CONN92_5 (0x09a0) +#define AFE_CONN93_5 (0x09a4) +#define AFE_CONN94_5 (0x09a8) +#define AFE_CONN95_5 (0x09ac) +#define AFE_CONN96_5 (0x09b0) +#define AFE_CONN97_5 (0x09b4) +#define AFE_CONN98_5 (0x09b8) +#define AFE_CONN99_5 (0x09bc) +#define AFE_CONN100_5 (0x09c0) +#define AFE_CONN101_5 (0x09c4) +#define AFE_CONN102_5 (0x09c8) +#define AFE_CONN103_5 (0x09cc) +#define AFE_CONN104_5 (0x09d0) +#define AFE_CONN105_5 (0x09d4) +#define AFE_CONN106_5 (0x09d8) +#define AFE_CONN107_5 (0x09dc) +#define AFE_CONN108_5 (0x09e0) +#define AFE_CONN109_5 (0x09e4) +#define AFE_CONN110_5 (0x09e8) +#define AFE_CONN111_5 (0x09ec) +#define AFE_CONN112_5 (0x09f0) +#define AFE_CONN113_5 (0x09f4) +#define AFE_CONN114_5 (0x09f8) +#define AFE_CONN115_5 (0x09fc) +#define AFE_CONN116_5 (0x0a00) +#define AFE_CONN117_5 (0x0a04) +#define AFE_CONN118_5 (0x0a08) +#define AFE_CONN119_5 (0x0a0c) +#define AFE_CONN120_5 (0x0a10) +#define AFE_CONN121_5 (0x0a14) +#define AFE_CONN122_5 (0x0a18) +#define AFE_CONN123_5 (0x0a1c) +#define AFE_CONN124_5 (0x0a20) +#define AFE_CONN125_5 (0x0a24) +#define AFE_CONN126_5 (0x0a28) +#define AFE_CONN127_5 (0x0a2c) +#define AFE_CONN128_5 (0x0a30) +#define AFE_CONN129_5 (0x0a34) +#define AFE_CONN130_5 (0x0a38) +#define AFE_CONN131_5 (0x0a3c) +#define AFE_CONN132_5 (0x0a40) +#define AFE_CONN133_5 (0x0a44) +#define AFE_CONN134_5 (0x0a48) +#define AFE_CONN135_5 (0x0a4c) +#define AFE_CONN136_5 (0x0a50) +#define AFE_CONN137_5 (0x0a54) +#define AFE_CONN138_5 (0x0a58) +#define AFE_CONN139_5 (0x0a5c) +#define AFE_CONN_RS_5 (0x0a60) +#define AFE_CONN_DI_5 (0x0a64) +#define AFE_CONN_16BIT_5 (0x0a68) +#define AFE_CONN_24BIT_5 (0x0a6c) +#define AFE_SECURE_MASK_CONN53_5 (0x0a70) +#define AFE_SECURE_MASK_CONN54_5 (0x0a74) +#define AFE_SECURE_MASK_CONN55_5 (0x0a78) +#define AFE_SECURE_MASK_CONN56_5 (0x0a7c) +#define AFE_SECURE_MASK_CONN57_5 (0x0a80) +#define AFE_SECURE_MASK_CONN58_5 (0x0a84) +#define AFE_SECURE_MASK_CONN59_5 (0x0a88) +#define AFE_SECURE_MASK_CONN60_5 (0x0a8c) +#define AFE_SECURE_MASK_CONN61_5 (0x0a90) +#define AFE_SECURE_MASK_CONN62_5 (0x0a94) +#define AFE_SECURE_MASK_CONN63_5 (0x0a98) +#define AFE_SECURE_MASK_CONN64_5 (0x0a9c) +#define AFE_SECURE_MASK_CONN65_5 (0x0aa0) +#define AFE_SECURE_MASK_CONN66_5 (0x0aa4) +#define AFE_SECURE_MASK_CONN67_5 (0x0aa8) +#define AFE_SECURE_MASK_CONN68_5 (0x0aac) +#define AFE_SECURE_MASK_CONN69_5 (0x0ab0) +#define AFE_SECURE_MASK_CONN70_5 (0x0ab4) +#define AFE_SECURE_MASK_CONN71_5 (0x0ab8) +#define AFE_SECURE_MASK_CONN72_5 (0x0abc) +#define AFE_SECURE_MASK_CONN73_5 (0x0ac0) +#define AFE_SECURE_MASK_CONN74_5 (0x0ac4) +#define AFE_SECURE_MASK_CONN75_5 (0x0ac8) +#define AFE_SECURE_MASK_CONN76_5 (0x0acc) +#define AFE_SECURE_MASK_CONN77_5 (0x0ad0) +#define AFE_SECURE_MASK_CONN78_5 (0x0ad4) +#define AFE_SECURE_MASK_CONN79_5 (0x0ad8) +#define AFE_SECURE_MASK_CONN80_5 (0x0adc) +#define AFE_SECURE_MASK_CONN81_5 (0x0ae0) +#define AFE_SECURE_MASK_CONN82_5 (0x0ae4) +#define AFE_SECURE_MASK_CONN83_5 (0x0ae8) +#define AFE_SECURE_MASK_CONN84_5 (0x0aec) +#define AFE_SECURE_MASK_CONN85_5 (0x0af0) +#define AFE_SECURE_MASK_CONN86_5 (0x0af4) +#define AFE_SECURE_MASK_CONN87_5 (0x0af8) +#define AFE_SECURE_MASK_CONN88_5 (0x0afc) +#define AFE_SECURE_MASK_CONN89_5 (0x0b00) +#define AFE_SECURE_MASK_CONN90_5 (0x0b04) +#define AFE_SECURE_MASK_CONN91_5 (0x0b08) +#define AFE_SECURE_MASK_CONN92_5 (0x0b0c) +#define AFE_SECURE_MASK_CONN93_5 (0x0b10) +#define AFE_SECURE_MASK_CONN94_5 (0x0b14) +#define AFE_SECURE_MASK_CONN95_5 (0x0b18) +#define AFE_SECURE_MASK_CONN96_5 (0x0b1c) +#define AFE_SECURE_MASK_CONN97_5 (0x0b20) +#define AFE_SECURE_MASK_CONN98_5 (0x0b24) +#define AFE_SECURE_MASK_CONN99_5 (0x0b28) +#define AFE_SECURE_MASK_CONN100_5 (0x0b2c) +#define AFE_SECURE_MASK_CONN101_5 (0x0b30) +#define AFE_SECURE_MASK_CONN102_5 (0x0b34) +#define AFE_SECURE_MASK_CONN103_5 (0x0b38) +#define AFE_SECURE_MASK_CONN104_5 (0x0b3c) +#define AFE_SECURE_MASK_CONN105_5 (0x0b40) +#define AFE_SECURE_MASK_CONN106_5 (0x0b44) +#define AFE_SECURE_MASK_CONN107_5 (0x0b48) +#define AFE_SECURE_MASK_CONN108_5 (0x0b4c) +#define AFE_SECURE_MASK_CONN109_5 (0x0b50) +#define AFE_SECURE_MASK_CONN110_5 (0x0b54) +#define AFE_SECURE_MASK_CONN111_5 (0x0b58) +#define AFE_SECURE_MASK_CONN112_5 (0x0b5c) +#define AFE_SECURE_MASK_CONN113_5 (0x0b60) +#define AFE_SECURE_MASK_CONN114_5 (0x0b64) +#define AFE_SECURE_MASK_CONN115_5 (0x0b68) +#define AFE_SECURE_MASK_CONN116_5 (0x0b6c) +#define AFE_SECURE_MASK_CONN117_5 (0x0b70) +#define AFE_SECURE_MASK_CONN118_5 (0x0b74) +#define AFE_SECURE_MASK_CONN119_5 (0x0b78) +#define AFE_SECURE_MASK_CONN120_5 (0x0b7c) +#define AFE_SECURE_MASK_CONN121_5 (0x0b80) +#define AFE_SECURE_MASK_CONN122_5 (0x0b84) +#define AFE_SECURE_MASK_CONN123_5 (0x0b88) +#define AFE_SECURE_MASK_CONN124_5 (0x0b8c) +#define AFE_SECURE_MASK_CONN125_5 (0x0b90) +#define AFE_SECURE_MASK_CONN126_5 (0x0b94) +#define AFE_SECURE_MASK_CONN127_5 (0x0b98) +#define AFE_SECURE_MASK_CONN128_5 (0x0b9c) +#define AFE_SECURE_MASK_CONN129_5 (0x0ba0) +#define AFE_SECURE_MASK_CONN130_5 (0x0ba4) +#define AFE_SECURE_MASK_CONN131_5 (0x0ba8) +#define AFE_SECURE_MASK_CONN132_5 (0x0bac) +#define AFE_SECURE_MASK_CONN133_5 (0x0bb0) +#define AFE_SECURE_MASK_CONN134_5 (0x0bb4) +#define AFE_SECURE_MASK_CONN135_5 (0x0bb8) +#define AFE_SECURE_MASK_CONN136_5 (0x0bbc) +#define AFE_SECURE_MASK_CONN137_5 (0x0bc0) +#define AFE_SECURE_MASK_CONN138_5 (0x0bc4) +#define AFE_SECURE_MASK_CONN139_5 (0x0bc8) +#define AFE_SECURE_MASK_CONN_RS_5 (0x0bcc) +#define AFE_SECURE_MASK_CONN_16BIT_5 (0x0bd0) +#define AFE_SECURE_MASK_CONN_24BIT_5 (0x0bd4) +#define AFE_ASRC11_NEW_CON0 (0x0d80) +#define AFE_ASRC11_NEW_CON1 (0x0d84) +#define AFE_ASRC11_NEW_CON2 (0x0d88) +#define AFE_ASRC11_NEW_CON3 (0x0d8c) +#define AFE_ASRC11_NEW_CON4 (0x0d90) +#define AFE_ASRC11_NEW_CON5 (0x0d94) +#define AFE_ASRC11_NEW_CON6 (0x0d98) +#define AFE_ASRC11_NEW_CON7 (0x0d9c) +#define AFE_ASRC11_NEW_CON8 (0x0da0) +#define AFE_ASRC11_NEW_CON9 (0x0da4) +#define AFE_ASRC11_NEW_CON10 (0x0da8) +#define AFE_ASRC11_NEW_CON11 (0x0dac) +#define AFE_ASRC11_NEW_CON13 (0x0db4) +#define AFE_ASRC11_NEW_CON14 (0x0db8) +#define AFE_ASRC12_NEW_CON0 (0x0dc0) +#define AFE_ASRC12_NEW_CON1 (0x0dc4) +#define AFE_ASRC12_NEW_CON2 (0x0dc8) +#define AFE_ASRC12_NEW_CON3 (0x0dcc) +#define AFE_ASRC12_NEW_CON4 (0x0dd0) +#define AFE_ASRC12_NEW_CON5 (0x0dd4) +#define AFE_ASRC12_NEW_CON6 (0x0dd8) +#define AFE_ASRC12_NEW_CON7 (0x0ddc) +#define AFE_ASRC12_NEW_CON8 (0x0de0) +#define AFE_ASRC12_NEW_CON9 (0x0de4) +#define AFE_ASRC12_NEW_CON10 (0x0de8) +#define AFE_ASRC12_NEW_CON11 (0x0dec) +#define AFE_ASRC12_NEW_CON13 (0x0df4) +#define AFE_ASRC12_NEW_CON14 (0x0df8) +#define AFE_SECURE_MASK_CONN176 (0x0fe0) +#define AFE_SECURE_MASK_CONN176_1 (0x0fe4) +#define AFE_SECURE_MASK_CONN176_2 (0x0fe8) +#define AFE_SECURE_MASK_CONN176_3 (0x0fec) +#define AFE_SECURE_MASK_CONN176_4 (0x0ff0) +#define AFE_SECURE_MASK_CONN176_5 (0x0ff4) +#define AFE_SECURE_MASK_CONN177 (0x0ff8) +#define AFE_SECURE_MASK_CONN177_1 (0x0ffc) +#define AFE_LRCK_CNT (0x1018) +#define AFE_SECURE_MASK_CONN177_2 (0x1020) +#define AFE_SECURE_MASK_CONN177_3 (0x1024) +#define AFE_SECURE_MASK_CONN177_4 (0x1028) +#define AFE_SECURE_MASK_CONN177_5 (0x102c) +#define AFE_SECURE_MASK_CONN182 (0x1090) +#define AFE_SECURE_MASK_CONN182_1 (0x1094) +#define AFE_SECURE_MASK_CONN182_2 (0x1098) +#define AFE_SECURE_MASK_CONN182_3 (0x109c) +#define AFE_SECURE_MASK_CONN182_4 (0x10a0) +#define AFE_SECURE_MASK_CONN182_5 (0x10a4) +#define AFE_SECURE_MASK_CONN183 (0x10a8) +#define AFE_SECURE_MASK_CONN183_1 (0x10ac) +#define AFE_SECURE_MASK_CONN183_2 (0x10b0) +#define AFE_SECURE_MASK_CONN183_3 (0x10b4) +#define AFE_SECURE_MASK_CONN183_4 (0x10b8) +#define AFE_SECURE_MASK_CONN183_5 (0x10bc) +#define AFE_DAC_CON0 (0x1200) +#define AFE_DAC_CON1 (0x1204) +#define AFE_DAC_CON2 (0x1208) +#define AFE_DAC_MON0 (0x1218) +#define AFE_DL1_BASE (0x1240) +#define AFE_DL1_CUR (0x1244) +#define AFE_DL1_END (0x1248) +#define AFE_DL1_CON0 (0x124c) +#define AFE_DL2_BASE (0x1250) +#define AFE_DL2_CUR (0x1254) +#define AFE_DL2_END (0x1258) +#define AFE_DL2_CON0 (0x125c) +#define AFE_DL3_BASE (0x1260) +#define AFE_DL3_CUR (0x1264) +#define AFE_DL3_END (0x1268) +#define AFE_DL3_CON0 (0x126c) +#define AFE_DL6_BASE (0x1290) +#define AFE_DL6_CUR (0x1294) +#define AFE_DL6_END (0x1298) +#define AFE_DL6_CON0 (0x129c) +#define AFE_DL7_BASE (0x12a0) +#define AFE_DL7_CUR (0x12a4) +#define AFE_DL7_END (0x12a8) +#define AFE_DL7_CON0 (0x12ac) +#define AFE_DL8_BASE (0x12b0) +#define AFE_DL8_CUR (0x12b4) +#define AFE_DL8_END (0x12b8) +#define AFE_DL8_CON0 (0x12bc) +#define AFE_DL10_BASE (0x12d0) +#define AFE_DL10_CUR (0x12d4) +#define AFE_DL10_END (0x12d8) +#define AFE_DL10_CON0 (0x12dc) +#define AFE_DL11_BASE (0x12e0) +#define AFE_DL11_CUR (0x12e4) +#define AFE_DL11_END (0x12e8) +#define AFE_DL11_CON0 (0x12ec) +#define AFE_UL1_BASE (0x1300) +#define AFE_UL1_CUR (0x1304) +#define AFE_UL1_END (0x1308) +#define AFE_UL1_CON0 (0x130c) +#define AFE_UL2_BASE (0x1310) +#define AFE_UL2_CUR (0x1314) +#define AFE_UL2_END (0x1318) +#define AFE_UL2_CON0 (0x131c) +#define AFE_UL3_BASE (0x1320) +#define AFE_UL3_CUR (0x1324) +#define AFE_UL3_END (0x1328) +#define AFE_UL3_CON0 (0x132c) +#define AFE_UL4_BASE (0x1330) +#define AFE_UL4_CUR (0x1334) +#define AFE_UL4_END (0x1338) +#define AFE_UL4_CON0 (0x133c) +#define AFE_UL5_BASE (0x1340) +#define AFE_UL5_CUR (0x1344) +#define AFE_UL5_END (0x1348) +#define AFE_UL5_CON0 (0x134c) +#define AFE_UL6_BASE (0x1350) +#define AFE_UL6_CUR (0x1354) +#define AFE_UL6_END (0x1358) +#define AFE_UL6_CON0 (0x135c) +#define AFE_UL8_BASE (0x1370) +#define AFE_UL8_CUR (0x1374) +#define AFE_UL8_END (0x1378) +#define AFE_UL8_CON0 (0x137c) +#define AFE_UL9_BASE (0x1380) +#define AFE_UL9_CUR (0x1384) +#define AFE_UL9_END (0x1388) +#define AFE_UL9_CON0 (0x138c) +#define AFE_UL10_BASE (0x13d0) +#define AFE_UL10_CUR (0x13d4) +#define AFE_UL10_END (0x13d8) +#define AFE_UL10_CON0 (0x13dc) +#define AFE_DL8_CHK_SUM1 (0x1400) +#define AFE_DL8_CHK_SUM2 (0x1404) +#define AFE_DL8_CHK_SUM3 (0x1408) +#define AFE_DL8_CHK_SUM4 (0x140c) +#define AFE_DL8_CHK_SUM5 (0x1410) +#define AFE_DL8_CHK_SUM6 (0x1414) +#define AFE_DL10_CHK_SUM1 (0x1418) +#define AFE_DL10_CHK_SUM2 (0x141c) +#define AFE_DL10_CHK_SUM3 (0x1420) +#define AFE_DL10_CHK_SUM4 (0x1424) +#define AFE_DL10_CHK_SUM5 (0x1428) +#define AFE_DL10_CHK_SUM6 (0x142c) +#define AFE_DL11_CHK_SUM1 (0x1430) +#define AFE_DL11_CHK_SUM2 (0x1434) +#define AFE_DL11_CHK_SUM3 (0x1438) +#define AFE_DL11_CHK_SUM4 (0x143c) +#define AFE_DL11_CHK_SUM5 (0x1440) +#define AFE_DL11_CHK_SUM6 (0x1444) +#define AFE_UL1_CHK_SUM1 (0x1450) +#define AFE_UL1_CHK_SUM2 (0x1454) +#define AFE_UL2_CHK_SUM1 (0x1458) +#define AFE_UL2_CHK_SUM2 (0x145c) +#define AFE_UL3_CHK_SUM1 (0x1460) +#define AFE_UL3_CHK_SUM2 (0x1464) +#define AFE_UL4_CHK_SUM1 (0x1468) +#define AFE_UL4_CHK_SUM2 (0x146c) +#define AFE_UL5_CHK_SUM1 (0x1470) +#define AFE_UL5_CHK_SUM2 (0x1474) +#define AFE_UL6_CHK_SUM1 (0x1478) +#define AFE_UL6_CHK_SUM2 (0x147c) +#define AFE_UL8_CHK_SUM1 (0x1488) +#define AFE_UL8_CHK_SUM2 (0x148c) +#define AFE_DL1_CHK_SUM1 (0x1490) +#define AFE_DL1_CHK_SUM2 (0x1494) +#define AFE_DL2_CHK_SUM1 (0x14a0) +#define AFE_DL2_CHK_SUM2 (0x14a4) +#define AFE_DL3_CHK_SUM1 (0x14b0) +#define AFE_DL3_CHK_SUM2 (0x14b4) +#define AFE_DL6_CHK_SUM1 (0x14e0) +#define AFE_DL6_CHK_SUM2 (0x14e4) +#define AFE_DL7_CHK_SUM1 (0x14f0) +#define AFE_DL7_CHK_SUM2 (0x14f4) +#define AFE_UL9_CHK_SUM1 (0x1528) +#define AFE_UL9_CHK_SUM2 (0x152c) +#define AFE_BUS_MON1 (0x1540) +#define AFE_UL10_CHK_SUM1 (0x1550) +#define AFE_UL10_CHK_SUM2 (0x1554) +#define UL1_MOD2AGT_CNT_LAT (0x1568) +#define UL2_MOD2AGT_CNT_LAT (0x156c) +#define UL3_MOD2AGT_CNT_LAT (0x1570) +#define UL4_MOD2AGT_CNT_LAT (0x1574) +#define UL5_MOD2AGT_CNT_LAT (0x1578) +#define UL6_MOD2AGT_CNT_LAT (0x157c) +#define UL8_MOD2AGT_CNT_LAT (0x1588) +#define UL9_MOD2AGT_CNT_LAT (0x158c) +#define UL10_MOD2AGT_CNT_LAT (0x1590) +#define AFE_MEMIF_AGENT_FS_CON0 (0x15a0) +#define AFE_MEMIF_AGENT_FS_CON1 (0x15a4) +#define AFE_MEMIF_AGENT_FS_CON2 (0x15a8) +#define AFE_MEMIF_AGENT_FS_CON3 (0x15ac) +#define AFE_MEMIF_BURST_CFG (0x1600) +#define AFE_MEMIF_BUF_FULL_MON (0x1610) +#define AFE_MEMIF_BUF_MON0 (0x1618) +#define AFE_MEMIF_BUF_MON1 (0x161c) +#define AFE_MEMIF_BUF_MON3 (0x1624) +#define AFE_MEMIF_BUF_MON4 (0x1628) +#define AFE_MEMIF_BUF_MON5 (0x162c) +#define AFE_MEMIF_BUF_MON6 (0x1630) +#define AFE_MEMIF_BUF_MON7 (0x1634) +#define AFE_MEMIF_BUF_MON8 (0x1638) +#define AFE_MEMIF_BUF_MON9 (0x163c) +#define AFE_MEMIF_BUF_MON10 (0x1640) +#define DL1_AGENT2MODULE_CNT (0x1674) +#define DL2_AGENT2MODULE_CNT (0x1678) +#define DL3_AGENT2MODULE_CNT (0x167c) +#define DL6_AGENT2MODULE_CNT (0x1688) +#define DL7_AGENT2MODULE_CNT (0x168c) +#define DL8_AGENT2MODULE_CNT (0x1690) +#define DL10_AGENT2MODULE_CNT (0x1698) +#define DL11_AGENT2MODULE_CNT (0x169c) +#define UL1_MODULE2AGENT_CNT (0x16a0) +#define UL2_MODULE2AGENT_CNT (0x16a4) +#define UL3_MODULE2AGENT_CNT (0x16a8) +#define UL4_MODULE2AGENT_CNT (0x16ac) +#define UL5_MODULE2AGENT_CNT (0x16b0) +#define UL6_MODULE2AGENT_CNT (0x16b4) +#define UL8_MODULE2AGENT_CNT (0x16bc) +#define UL9_MODULE2AGENT_CNT (0x16c0) +#define UL10_MODULE2AGENT_CNT (0x16c4) +#define AFE_SECURE_CON2 (0x1798) +#define AFE_SECURE_CON1 (0x179c) +#define AFE_SECURE_CON (0x17a0) +#define AFE_SRAM_BOUND (0x17a4) +#define AFE_SE_SECURE_CON (0x17a8) +#define AFE_SECURE_MASK_LOOPBACK (0x17bc) +#define AFE_SRAM_SECURE_CON (0x1800) +#define AFE_SRAM_SECURE_CON1 (0x1804) +#define AFE_SRAM_SECURE_CON2 (0x1808) +#define AFE_SECURE_SIDEBAND0 (0x1908) +#define AFE_SECURE_SIDEBAND1 (0x190c) +#define AFE_SECURE_SIDEBAND2 (0x1910) +#define AFE_SECURE_SIDEBAND3 (0x1914) +#define AFE_SECURE_MASK_BASE_ADR_MSB (0x1920) +#define AFE_SECURE_MASK_END_ADR_MSB (0x1924) +#define AFE_NORMAL_BASE_ADR_MSB (0x192c) +#define AFE_NORMAL_END_ADR_MSB (0x1930) +#define AFE_SECURE_MASK_LOOPBACK0 (0x1940) +#define AFE_SECURE_MASK_LOOPBACK1 (0x1944) +#define AFE_SECURE_MASK_LOOPBACK2 (0x1948) +#define AFE_LOOPBACK_CFG0 (0x1950) +#define AFE_LOOPBACK_CFG1 (0x1954) +#define AFE_LOOPBACK_CFG2 (0x1958) +#define AFE_DMIC0_UL_SRC_CON0 (0x1a00) +#define AFE_DMIC0_UL_SRC_CON1 (0x1a04) +#define AFE_DMIC0_SRC_DEBUG (0x1a08) +#define AFE_DMIC0_SRC_DEBUG_MON0 (0x1a0c) +#define AFE_DMIC0_UL_SRC_MON0 (0x1a10) +#define AFE_DMIC0_UL_SRC_MON1 (0x1a14) +#define AFE_DMIC0_IIR_COEF_02_01 (0x1a18) +#define AFE_DMIC0_IIR_COEF_04_03 (0x1a1c) +#define AFE_DMIC0_IIR_COEF_06_05 (0x1a20) +#define AFE_DMIC0_IIR_COEF_08_07 (0x1a24) +#define AFE_DMIC0_IIR_COEF_10_09 (0x1a28) +#define AFE_DMIC1_UL_SRC_CON0 (0x1a68) +#define AFE_DMIC1_UL_SRC_CON1 (0x1a6c) +#define AFE_DMIC1_SRC_DEBUG (0x1a70) +#define AFE_DMIC1_SRC_DEBUG_MON0 (0x1a74) +#define AFE_DMIC1_UL_SRC_MON0 (0x1a78) +#define AFE_DMIC1_UL_SRC_MON1 (0x1a7c) +#define AFE_DMIC1_IIR_COEF_02_01 (0x1a80) +#define AFE_DMIC1_IIR_COEF_04_03 (0x1a84) +#define AFE_DMIC1_IIR_COEF_06_05 (0x1a88) +#define AFE_DMIC1_IIR_COEF_08_07 (0x1a8c) +#define AFE_DMIC1_IIR_COEF_10_09 (0x1a90) +#define AFE_DMIC2_UL_SRC_CON0 (0x1ad0) +#define AFE_DMIC2_UL_SRC_CON1 (0x1ad4) +#define AFE_DMIC2_SRC_DEBUG (0x1ad8) +#define AFE_DMIC2_SRC_DEBUG_MON0 (0x1adc) +#define AFE_DMIC2_UL_SRC_MON0 (0x1ae0) +#define AFE_DMIC2_UL_SRC_MON1 (0x1ae4) +#define AFE_DMIC2_IIR_COEF_02_01 (0x1ae8) +#define AFE_DMIC2_IIR_COEF_04_03 (0x1aec) +#define AFE_DMIC2_IIR_COEF_06_05 (0x1af0) +#define AFE_DMIC2_IIR_COEF_08_07 (0x1af4) +#define AFE_DMIC2_IIR_COEF_10_09 (0x1af8) +#define AFE_DMIC3_UL_SRC_CON0 (0x1b38) +#define AFE_DMIC3_UL_SRC_CON1 (0x1b3c) +#define AFE_DMIC3_SRC_DEBUG (0x1b40) +#define AFE_DMIC3_SRC_DEBUG_MON0 (0x1b44) +#define AFE_DMIC3_UL_SRC_MON0 (0x1b48) +#define AFE_DMIC3_UL_SRC_MON1 (0x1b4c) +#define AFE_DMIC3_IIR_COEF_02_01 (0x1b50) +#define AFE_DMIC3_IIR_COEF_04_03 (0x1b54) +#define AFE_DMIC3_IIR_COEF_06_05 (0x1b58) +#define AFE_DMIC3_IIR_COEF_08_07 (0x1b5c) +#define AFE_DMIC3_IIR_COEF_10_09 (0x1b60) +#define DMIC_BYPASS_HW_GAIN (0x1bf0) +#define DMIC_GAIN1_CON0 (0x1c00) +#define DMIC_GAIN1_CON1 (0x1c04) +#define DMIC_GAIN1_CON2 (0x1c08) +#define DMIC_GAIN1_CON3 (0x1c0c) +#define DMIC_GAIN1_CUR (0x1c10) +#define DMIC_GAIN2_CON0 (0x1c20) +#define DMIC_GAIN2_CON1 (0x1c24) +#define DMIC_GAIN2_CON2 (0x1c28) +#define DMIC_GAIN2_CON3 (0x1c2c) +#define DMIC_GAIN2_CUR (0x1c30) +#define DMIC_GAIN3_CON0 (0x1c40) +#define DMIC_GAIN3_CON1 (0x1c44) +#define DMIC_GAIN3_CON2 (0x1c48) +#define DMIC_GAIN3_CON3 (0x1c4c) +#define DMIC_GAIN3_CUR (0x1c50) +#define DMIC_GAIN4_CON0 (0x1c60) +#define DMIC_GAIN4_CON1 (0x1c64) +#define DMIC_GAIN4_CON2 (0x1c68) +#define DMIC_GAIN4_CON3 (0x1c6c) +#define DMIC_GAIN4_CUR (0x1c70) +#define ETDM_OUT1_DSD_FADE_CON (0x2260) +#define ETDM_OUT1_DSD_FADE_CON1 (0x2264) +#define ETDM_OUT3_DSD_FADE_CON (0x2280) +#define ETDM_OUT3_DSD_FADE_CON1 (0x2284) +#define ETDM_IN1_AFIFO_CON (0x2294) +#define ETDM_IN2_AFIFO_CON (0x2298) +#define ETDM_IN1_MONITOR (0x22c0) +#define ETDM_IN2_MONITOR (0x22c4) +#define ETDM_OUT1_MONITOR (0x22d0) +#define ETDM_OUT2_MONITOR (0x22d4) +#define ETDM_OUT3_MONITOR (0x22d8) +#define ETDM_COWORK_SEC_CON0 (0x22e0) +#define ETDM_COWORK_SEC_CON1 (0x22e4) +#define ETDM_COWORK_SEC_CON2 (0x22e8) +#define ETDM_COWORK_SEC_CON3 (0x22ec) +#define ETDM_COWORK_CON0 (0x22f0) +#define ETDM_COWORK_CON1 (0x22f4) +#define ETDM_COWORK_CON2 (0x22f8) +#define ETDM_COWORK_CON3 (0x22fc) +#define ETDM_IN1_CON0 (0x2300) +#define ETDM_IN1_CON1 (0x2304) +#define ETDM_IN1_CON2 (0x2308) +#define ETDM_IN1_CON3 (0x230c) +#define ETDM_IN1_CON4 (0x2310) +#define ETDM_IN1_CON5 (0x2314) +#define ETDM_IN1_CON6 (0x2318) +#define ETDM_IN1_CON7 (0x231c) +#define ETDM_IN2_CON0 (0x2320) +#define ETDM_IN2_CON1 (0x2324) +#define ETDM_IN2_CON2 (0x2328) +#define ETDM_IN2_CON3 (0x232c) +#define ETDM_IN2_CON4 (0x2330) +#define ETDM_IN2_CON5 (0x2334) +#define ETDM_IN2_CON6 (0x2338) +#define ETDM_IN2_CON7 (0x233c) +#define ETDM_OUT1_CON0 (0x2380) +#define ETDM_OUT1_CON1 (0x2384) +#define ETDM_OUT1_CON2 (0x2388) +#define ETDM_OUT1_CON3 (0x238c) +#define ETDM_OUT1_CON4 (0x2390) +#define ETDM_OUT1_CON5 (0x2394) +#define ETDM_OUT1_CON6 (0x2398) +#define ETDM_OUT1_CON7 (0x239c) +#define ETDM_OUT2_CON0 (0x23a0) +#define ETDM_OUT2_CON1 (0x23a4) +#define ETDM_OUT2_CON2 (0x23a8) +#define ETDM_OUT2_CON3 (0x23ac) +#define ETDM_OUT2_CON4 (0x23b0) +#define ETDM_OUT2_CON5 (0x23b4) +#define ETDM_OUT2_CON6 (0x23b8) +#define ETDM_OUT2_CON7 (0x23bc) +#define ETDM_OUT3_CON0 (0x23c0) +#define ETDM_OUT3_CON1 (0x23c4) +#define ETDM_OUT3_CON2 (0x23c8) +#define ETDM_OUT3_CON3 (0x23cc) +#define ETDM_OUT3_CON4 (0x23d0) +#define ETDM_OUT3_CON5 (0x23d4) +#define ETDM_OUT3_CON6 (0x23d8) +#define ETDM_OUT3_CON7 (0x23dc) +#define ETDM_OUT3_CON8 (0x23e0) +#define ETDM_OUT1_CON8 (0x23e4) +#define ETDM_OUT2_CON8 (0x23e8) +#define GASRC_TIMING_CON0 (0x2414) +#define GASRC_TIMING_CON1 (0x2418) +#define GASRC_TIMING_CON2 (0x241c) +#define GASRC_TIMING_CON3 (0x2420) +#define GASRC_TIMING_CON4 (0x2424) +#define GASRC_TIMING_CON5 (0x2428) +#define A3_A4_TIMING_SEL0 (0x2440) +#define A3_A4_TIMING_SEL1 (0x2444) +#define A3_A4_TIMING_SEL2 (0x2448) +#define A3_A4_TIMING_SEL3 (0x244c) +#define A3_A4_TIMING_SEL4 (0x2450) +#define A3_A4_TIMING_SEL5 (0x2454) +#define A3_A4_TIMING_SEL6 (0x2458) +#define ASYS_TOP_DEBUG (0x2500) +#define AFE_DPTX_CON (0x2558) +#define AFE_DPTX_MON (0x255c) +#define AFE_ADDA_DL_SRC2_CON0 (0x2d00) +#define AFE_ADDA_DL_SRC2_CON1 (0x2d04) +#define AFE_ADDA_TOP_CON0 (0x2d0c) +#define AFE_ADDA_UL_DL_CON0 (0x2d10) +#define AFE_ADDA_SRC_DEBUG (0x2d14) +#define AFE_ADDA_SRC_DEBUG_MON0 (0x2d18) +#define AFE_ADDA_SRC_DEBUG_MON1 (0x2d20) +#define AFE_ADDA_PREDIS_CON0 (0x2d24) +#define AFE_ADDA_PREDIS_CON1 (0x2d28) +#define AFE_ADDA_PREDIS_CON2 (0x2d2c) +#define AFE_ADDA_PREDIS_CON3 (0x2d30) +#define AFE_ADDA_DL_SDM_DCCOMP_CON (0x2d34) +#define AFE_ADDA_DL_SDM_TEST (0x2d38) +#define AFE_ADDA_DL_DC_COMP_CFG0 (0x2d3c) +#define AFE_ADDA_DL_DC_COMP_CFG1 (0x2d40) +#define AFE_ADDA_DL_SDM_FIFO_MON (0x2d44) +#define AFE_ADDA_DL_SRC_LCH_MON (0x2d50) +#define AFE_ADDA_DL_SRC_RCH_MON (0x2d54) +#define AFE_ADDA_DL_SDM_OUT_MON (0x2d58) +#define AFE_ADDA_DL_SDM_DITHER_CON (0x2d5c) +#define AFE_ADDA_DL_SDM_AUTO_RESET_CON (0x2d60) +#define AFE_ADDA_UL_SRC_CON0 (0x2e3c) +#define AFE_ADDA_UL_SRC_CON1 (0x2e40) +#define AFE_CONN0 (0x3000) +#define AFE_CONN0_1 (0x3004) +#define AFE_CONN0_2 (0x3008) +#define AFE_CONN0_3 (0x300c) +#define AFE_CONN0_4 (0x3010) +#define AFE_CONN1 (0x3014) +#define AFE_CONN1_1 (0x3018) +#define AFE_CONN1_2 (0x301c) +#define AFE_CONN1_3 (0x3020) +#define AFE_CONN1_4 (0x3024) +#define AFE_CONN2 (0x3028) +#define AFE_CONN2_1 (0x302c) +#define AFE_CONN2_2 (0x3030) +#define AFE_CONN2_3 (0x3034) +#define AFE_CONN2_4 (0x3038) +#define AFE_CONN3 (0x303c) +#define AFE_CONN3_1 (0x3040) +#define AFE_CONN3_2 (0x3044) +#define AFE_CONN3_3 (0x3048) +#define AFE_CONN3_4 (0x304c) +#define AFE_CONN4 (0x3050) +#define AFE_CONN4_1 (0x3054) +#define AFE_CONN4_2 (0x3058) +#define AFE_CONN4_3 (0x305c) +#define AFE_CONN4_4 (0x3060) +#define AFE_CONN5 (0x3064) +#define AFE_CONN5_1 (0x3068) +#define AFE_CONN5_2 (0x306c) +#define AFE_CONN5_3 (0x3070) +#define AFE_CONN5_4 (0x3074) +#define AFE_CONN6 (0x3078) +#define AFE_CONN6_1 (0x307c) +#define AFE_CONN6_2 (0x3080) +#define AFE_CONN6_3 (0x3084) +#define AFE_CONN6_4 (0x3088) +#define AFE_CONN7 (0x308c) +#define AFE_CONN7_1 (0x3090) +#define AFE_CONN7_2 (0x3094) +#define AFE_CONN7_3 (0x3098) +#define AFE_CONN7_4 (0x309c) +#define AFE_CONN8 (0x30a0) +#define AFE_CONN8_1 (0x30a4) +#define AFE_CONN8_2 (0x30a8) +#define AFE_CONN8_3 (0x30ac) +#define AFE_CONN8_4 (0x30b0) +#define AFE_CONN9 (0x30b4) +#define AFE_CONN9_1 (0x30b8) +#define AFE_CONN9_2 (0x30bc) +#define AFE_CONN9_3 (0x30c0) +#define AFE_CONN9_4 (0x30c4) +#define AFE_CONN10 (0x30c8) +#define AFE_CONN10_1 (0x30cc) +#define AFE_CONN10_2 (0x30d0) +#define AFE_CONN10_3 (0x30d4) +#define AFE_CONN10_4 (0x30d8) +#define AFE_CONN11 (0x30dc) +#define AFE_CONN11_1 (0x30e0) +#define AFE_CONN11_2 (0x30e4) +#define AFE_CONN11_3 (0x30e8) +#define AFE_CONN11_4 (0x30ec) +#define AFE_CONN12 (0x30f0) +#define AFE_CONN12_1 (0x30f4) +#define AFE_CONN12_2 (0x30f8) +#define AFE_CONN12_3 (0x30fc) +#define AFE_CONN12_4 (0x3100) +#define AFE_CONN13 (0x3104) +#define AFE_CONN13_1 (0x3108) +#define AFE_CONN13_2 (0x310c) +#define AFE_CONN13_3 (0x3110) +#define AFE_CONN13_4 (0x3114) +#define AFE_CONN14 (0x3118) +#define AFE_CONN14_1 (0x311c) +#define AFE_CONN14_2 (0x3120) +#define AFE_CONN14_3 (0x3124) +#define AFE_CONN14_4 (0x3128) +#define AFE_CONN15 (0x312c) +#define AFE_CONN15_1 (0x3130) +#define AFE_CONN15_2 (0x3134) +#define AFE_CONN15_3 (0x3138) +#define AFE_CONN15_4 (0x313c) +#define AFE_CONN16 (0x3140) +#define AFE_CONN16_1 (0x3144) +#define AFE_CONN16_2 (0x3148) +#define AFE_CONN16_3 (0x314c) +#define AFE_CONN16_4 (0x3150) +#define AFE_CONN17 (0x3154) +#define AFE_CONN17_1 (0x3158) +#define AFE_CONN17_2 (0x315c) +#define AFE_CONN17_3 (0x3160) +#define AFE_CONN17_4 (0x3164) +#define AFE_CONN18 (0x3168) +#define AFE_CONN18_1 (0x316c) +#define AFE_CONN18_2 (0x3170) +#define AFE_CONN18_3 (0x3174) +#define AFE_CONN18_4 (0x3178) +#define AFE_CONN19 (0x317c) +#define AFE_CONN19_1 (0x3180) +#define AFE_CONN19_2 (0x3184) +#define AFE_CONN19_3 (0x3188) +#define AFE_CONN19_4 (0x318c) +#define AFE_CONN20 (0x3190) +#define AFE_CONN20_1 (0x3194) +#define AFE_CONN20_2 (0x3198) +#define AFE_CONN20_3 (0x319c) +#define AFE_CONN20_4 (0x31a0) +#define AFE_CONN21 (0x31a4) +#define AFE_CONN21_1 (0x31a8) +#define AFE_CONN21_2 (0x31ac) +#define AFE_CONN21_3 (0x31b0) +#define AFE_CONN21_4 (0x31b4) +#define AFE_CONN22 (0x31b8) +#define AFE_CONN22_1 (0x31bc) +#define AFE_CONN22_2 (0x31c0) +#define AFE_CONN22_3 (0x31c4) +#define AFE_CONN22_4 (0x31c8) +#define AFE_CONN23 (0x31cc) +#define AFE_CONN23_1 (0x31d0) +#define AFE_CONN23_2 (0x31d4) +#define AFE_CONN23_3 (0x31d8) +#define AFE_CONN23_4 (0x31dc) +#define AFE_CONN24 (0x31e0) +#define AFE_CONN24_1 (0x31e4) +#define AFE_CONN24_2 (0x31e8) +#define AFE_CONN24_3 (0x31ec) +#define AFE_CONN24_4 (0x31f0) +#define AFE_CONN25 (0x31f4) +#define AFE_CONN25_1 (0x31f8) +#define AFE_CONN25_2 (0x31fc) +#define AFE_CONN25_3 (0x3200) +#define AFE_CONN25_4 (0x3204) +#define AFE_CONN26 (0x3208) +#define AFE_CONN26_1 (0x320c) +#define AFE_CONN26_2 (0x3210) +#define AFE_CONN26_3 (0x3214) +#define AFE_CONN26_4 (0x3218) +#define AFE_CONN27 (0x321c) +#define AFE_CONN27_1 (0x3220) +#define AFE_CONN27_2 (0x3224) +#define AFE_CONN27_3 (0x3228) +#define AFE_CONN27_4 (0x322c) +#define AFE_CONN28 (0x3230) +#define AFE_CONN28_1 (0x3234) +#define AFE_CONN28_2 (0x3238) +#define AFE_CONN28_3 (0x323c) +#define AFE_CONN28_4 (0x3240) +#define AFE_CONN29 (0x3244) +#define AFE_CONN29_1 (0x3248) +#define AFE_CONN29_2 (0x324c) +#define AFE_CONN29_3 (0x3250) +#define AFE_CONN29_4 (0x3254) +#define AFE_CONN30 (0x3258) +#define AFE_CONN30_1 (0x325c) +#define AFE_CONN30_2 (0x3260) +#define AFE_CONN30_3 (0x3264) +#define AFE_CONN30_4 (0x3268) +#define AFE_CONN31 (0x326c) +#define AFE_CONN31_1 (0x3270) +#define AFE_CONN31_2 (0x3274) +#define AFE_CONN31_3 (0x3278) +#define AFE_CONN31_4 (0x327c) +#define AFE_CONN32 (0x3280) +#define AFE_CONN32_1 (0x3284) +#define AFE_CONN32_2 (0x3288) +#define AFE_CONN32_3 (0x328c) +#define AFE_CONN32_4 (0x3290) +#define AFE_CONN33 (0x3294) +#define AFE_CONN33_1 (0x3298) +#define AFE_CONN33_2 (0x329c) +#define AFE_CONN33_3 (0x32a0) +#define AFE_CONN33_4 (0x32a4) +#define AFE_CONN34 (0x32a8) +#define AFE_CONN34_1 (0x32ac) +#define AFE_CONN34_2 (0x32b0) +#define AFE_CONN34_3 (0x32b4) +#define AFE_CONN34_4 (0x32b8) +#define AFE_CONN35 (0x32bc) +#define AFE_CONN35_1 (0x32c0) +#define AFE_CONN35_2 (0x32c4) +#define AFE_CONN35_3 (0x32c8) +#define AFE_CONN35_4 (0x32cc) +#define AFE_CONN36 (0x32d0) +#define AFE_CONN36_1 (0x32d4) +#define AFE_CONN36_2 (0x32d8) +#define AFE_CONN36_3 (0x32dc) +#define AFE_CONN36_4 (0x32e0) +#define AFE_CONN37 (0x32e4) +#define AFE_CONN37_1 (0x32e8) +#define AFE_CONN37_2 (0x32ec) +#define AFE_CONN37_3 (0x32f0) +#define AFE_CONN37_4 (0x32f4) +#define AFE_CONN38 (0x32f8) +#define AFE_CONN38_1 (0x32fc) +#define AFE_CONN38_2 (0x3300) +#define AFE_CONN38_3 (0x3304) +#define AFE_CONN38_4 (0x3308) +#define AFE_CONN39 (0x330c) +#define AFE_CONN39_1 (0x3310) +#define AFE_CONN39_2 (0x3314) +#define AFE_CONN39_3 (0x3318) +#define AFE_CONN39_4 (0x331c) +#define AFE_CONN40 (0x3320) +#define AFE_CONN40_1 (0x3324) +#define AFE_CONN40_2 (0x3328) +#define AFE_CONN40_3 (0x332c) +#define AFE_CONN40_4 (0x3330) +#define AFE_CONN41 (0x3334) +#define AFE_CONN41_1 (0x3338) +#define AFE_CONN41_2 (0x333c) +#define AFE_CONN41_3 (0x3340) +#define AFE_CONN41_4 (0x3344) +#define AFE_CONN42 (0x3348) +#define AFE_CONN42_1 (0x334c) +#define AFE_CONN42_2 (0x3350) +#define AFE_CONN42_3 (0x3354) +#define AFE_CONN42_4 (0x3358) +#define AFE_CONN43 (0x335c) +#define AFE_CONN43_1 (0x3360) +#define AFE_CONN43_2 (0x3364) +#define AFE_CONN43_3 (0x3368) +#define AFE_CONN43_4 (0x336c) +#define AFE_CONN44 (0x3370) +#define AFE_CONN44_1 (0x3374) +#define AFE_CONN44_2 (0x3378) +#define AFE_CONN44_3 (0x337c) +#define AFE_CONN44_4 (0x3380) +#define AFE_CONN45 (0x3384) +#define AFE_CONN45_1 (0x3388) +#define AFE_CONN45_2 (0x338c) +#define AFE_CONN45_3 (0x3390) +#define AFE_CONN45_4 (0x3394) +#define AFE_CONN46 (0x3398) +#define AFE_CONN46_1 (0x339c) +#define AFE_CONN46_2 (0x33a0) +#define AFE_CONN46_3 (0x33a4) +#define AFE_CONN46_4 (0x33a8) +#define AFE_CONN47 (0x33ac) +#define AFE_CONN47_1 (0x33b0) +#define AFE_CONN47_2 (0x33b4) +#define AFE_CONN47_3 (0x33b8) +#define AFE_CONN47_4 (0x33bc) +#define AFE_CONN48 (0x33c0) +#define AFE_CONN48_1 (0x33c4) +#define AFE_CONN48_2 (0x33c8) +#define AFE_CONN48_3 (0x33cc) +#define AFE_CONN48_4 (0x33d0) +#define AFE_CONN49 (0x33d4) +#define AFE_CONN49_1 (0x33d8) +#define AFE_CONN49_2 (0x33dc) +#define AFE_CONN49_3 (0x33e0) +#define AFE_CONN49_4 (0x33e4) +#define AFE_CONN50 (0x33e8) +#define AFE_CONN50_1 (0x33ec) +#define AFE_CONN50_2 (0x33f0) +#define AFE_CONN50_3 (0x33f4) +#define AFE_CONN50_4 (0x33f8) +#define AFE_CONN51 (0x33fc) +#define AFE_CONN51_1 (0x3400) +#define AFE_CONN51_2 (0x3404) +#define AFE_CONN51_3 (0x3408) +#define AFE_CONN51_4 (0x340c) +#define AFE_CONN52 (0x3410) +#define AFE_CONN52_1 (0x3414) +#define AFE_CONN52_2 (0x3418) +#define AFE_CONN52_3 (0x341c) +#define AFE_CONN52_4 (0x3420) +#define AFE_CONN53 (0x3424) +#define AFE_CONN53_1 (0x3428) +#define AFE_CONN53_2 (0x342c) +#define AFE_CONN53_3 (0x3430) +#define AFE_CONN53_4 (0x3434) +#define AFE_CONN54 (0x3438) +#define AFE_CONN54_1 (0x343c) +#define AFE_CONN54_2 (0x3440) +#define AFE_CONN54_3 (0x3444) +#define AFE_CONN54_4 (0x3448) +#define AFE_CONN55 (0x344c) +#define AFE_CONN55_1 (0x3450) +#define AFE_CONN55_2 (0x3454) +#define AFE_CONN55_3 (0x3458) +#define AFE_CONN55_4 (0x345c) +#define AFE_CONN56 (0x3460) +#define AFE_CONN56_1 (0x3464) +#define AFE_CONN56_2 (0x3468) +#define AFE_CONN56_3 (0x346c) +#define AFE_CONN56_4 (0x3470) +#define AFE_CONN57 (0x3474) +#define AFE_CONN57_1 (0x3478) +#define AFE_CONN57_2 (0x347c) +#define AFE_CONN57_3 (0x3480) +#define AFE_CONN57_4 (0x3484) +#define AFE_CONN58 (0x3488) +#define AFE_CONN58_1 (0x348c) +#define AFE_CONN58_2 (0x3490) +#define AFE_CONN58_3 (0x3494) +#define AFE_CONN58_4 (0x3498) +#define AFE_CONN59 (0x349c) +#define AFE_CONN59_1 (0x34a0) +#define AFE_CONN59_2 (0x34a4) +#define AFE_CONN59_3 (0x34a8) +#define AFE_CONN59_4 (0x34ac) +#define AFE_CONN60 (0x34b0) +#define AFE_CONN60_1 (0x34b4) +#define AFE_CONN60_2 (0x34b8) +#define AFE_CONN60_3 (0x34bc) +#define AFE_CONN60_4 (0x34c0) +#define AFE_CONN61 (0x34c4) +#define AFE_CONN61_1 (0x34c8) +#define AFE_CONN61_2 (0x34cc) +#define AFE_CONN61_3 (0x34d0) +#define AFE_CONN61_4 (0x34d4) +#define AFE_CONN62 (0x34d8) +#define AFE_CONN62_1 (0x34dc) +#define AFE_CONN62_2 (0x34e0) +#define AFE_CONN62_3 (0x34e4) +#define AFE_CONN62_4 (0x34e8) +#define AFE_CONN63 (0x34ec) +#define AFE_CONN63_1 (0x34f0) +#define AFE_CONN63_2 (0x34f4) +#define AFE_CONN63_3 (0x34f8) +#define AFE_CONN63_4 (0x34fc) +#define AFE_CONN64 (0x3500) +#define AFE_CONN64_1 (0x3504) +#define AFE_CONN64_2 (0x3508) +#define AFE_CONN64_3 (0x350c) +#define AFE_CONN64_4 (0x3510) +#define AFE_CONN65 (0x3514) +#define AFE_CONN65_1 (0x3518) +#define AFE_CONN65_2 (0x351c) +#define AFE_CONN65_3 (0x3520) +#define AFE_CONN65_4 (0x3524) +#define AFE_CONN66 (0x3528) +#define AFE_CONN66_1 (0x352c) +#define AFE_CONN66_2 (0x3530) +#define AFE_CONN66_3 (0x3534) +#define AFE_CONN66_4 (0x3538) +#define AFE_CONN67 (0x353c) +#define AFE_CONN67_1 (0x3540) +#define AFE_CONN67_2 (0x3544) +#define AFE_CONN67_3 (0x3548) +#define AFE_CONN67_4 (0x354c) +#define AFE_CONN68 (0x3550) +#define AFE_CONN68_1 (0x3554) +#define AFE_CONN68_2 (0x3558) +#define AFE_CONN68_3 (0x355c) +#define AFE_CONN68_4 (0x3560) +#define AFE_CONN69 (0x3564) +#define AFE_CONN69_1 (0x3568) +#define AFE_CONN69_2 (0x356c) +#define AFE_CONN69_3 (0x3570) +#define AFE_CONN69_4 (0x3574) +#define AFE_CONN70 (0x3578) +#define AFE_CONN70_1 (0x357c) +#define AFE_CONN70_2 (0x3580) +#define AFE_CONN70_3 (0x3584) +#define AFE_CONN70_4 (0x3588) +#define AFE_CONN71 (0x358c) +#define AFE_CONN71_1 (0x3590) +#define AFE_CONN71_2 (0x3594) +#define AFE_CONN71_3 (0x3598) +#define AFE_CONN71_4 (0x359c) +#define AFE_CONN72 (0x35a0) +#define AFE_CONN72_1 (0x35a4) +#define AFE_CONN72_2 (0x35a8) +#define AFE_CONN72_3 (0x35ac) +#define AFE_CONN72_4 (0x35b0) +#define AFE_CONN73 (0x35b4) +#define AFE_CONN73_1 (0x35b8) +#define AFE_CONN73_2 (0x35bc) +#define AFE_CONN73_3 (0x35c0) +#define AFE_CONN73_4 (0x35c4) +#define AFE_CONN74 (0x35c8) +#define AFE_CONN74_1 (0x35cc) +#define AFE_CONN74_2 (0x35d0) +#define AFE_CONN74_3 (0x35d4) +#define AFE_CONN74_4 (0x35d8) +#define AFE_CONN75 (0x35dc) +#define AFE_CONN75_1 (0x35e0) +#define AFE_CONN75_2 (0x35e4) +#define AFE_CONN75_3 (0x35e8) +#define AFE_CONN75_4 (0x35ec) +#define AFE_CONN76 (0x35f0) +#define AFE_CONN76_1 (0x35f4) +#define AFE_CONN76_2 (0x35f8) +#define AFE_CONN76_3 (0x35fc) +#define AFE_CONN76_4 (0x3600) +#define AFE_CONN77 (0x3604) +#define AFE_CONN77_1 (0x3608) +#define AFE_CONN77_2 (0x360c) +#define AFE_CONN77_3 (0x3610) +#define AFE_CONN77_4 (0x3614) +#define AFE_CONN78 (0x3618) +#define AFE_CONN78_1 (0x361c) +#define AFE_CONN78_2 (0x3620) +#define AFE_CONN78_3 (0x3624) +#define AFE_CONN78_4 (0x3628) +#define AFE_CONN79 (0x362c) +#define AFE_CONN79_1 (0x3630) +#define AFE_CONN79_2 (0x3634) +#define AFE_CONN79_3 (0x3638) +#define AFE_CONN79_4 (0x363c) +#define AFE_CONN80 (0x3640) +#define AFE_CONN80_1 (0x3644) +#define AFE_CONN80_2 (0x3648) +#define AFE_CONN80_3 (0x364c) +#define AFE_CONN80_4 (0x3650) +#define AFE_CONN81 (0x3654) +#define AFE_CONN81_1 (0x3658) +#define AFE_CONN81_2 (0x365c) +#define AFE_CONN81_3 (0x3660) +#define AFE_CONN81_4 (0x3664) +#define AFE_CONN82 (0x3668) +#define AFE_CONN82_1 (0x366c) +#define AFE_CONN82_2 (0x3670) +#define AFE_CONN82_3 (0x3674) +#define AFE_CONN82_4 (0x3678) +#define AFE_CONN83 (0x367c) +#define AFE_CONN83_1 (0x3680) +#define AFE_CONN83_2 (0x3684) +#define AFE_CONN83_3 (0x3688) +#define AFE_CONN83_4 (0x368c) +#define AFE_CONN84 (0x3690) +#define AFE_CONN84_1 (0x3694) +#define AFE_CONN84_2 (0x3698) +#define AFE_CONN84_3 (0x369c) +#define AFE_CONN84_4 (0x36a0) +#define AFE_CONN85 (0x36a4) +#define AFE_CONN85_1 (0x36a8) +#define AFE_CONN85_2 (0x36ac) +#define AFE_CONN85_3 (0x36b0) +#define AFE_CONN85_4 (0x36b4) +#define AFE_CONN86 (0x36b8) +#define AFE_CONN86_1 (0x36bc) +#define AFE_CONN86_2 (0x36c0) +#define AFE_CONN86_3 (0x36c4) +#define AFE_CONN86_4 (0x36c8) +#define AFE_CONN87 (0x36cc) +#define AFE_CONN87_1 (0x36d0) +#define AFE_CONN87_2 (0x36d4) +#define AFE_CONN87_3 (0x36d8) +#define AFE_CONN87_4 (0x36dc) +#define AFE_CONN88 (0x36e0) +#define AFE_CONN88_1 (0x36e4) +#define AFE_CONN88_2 (0x36e8) +#define AFE_CONN88_3 (0x36ec) +#define AFE_CONN88_4 (0x36f0) +#define AFE_CONN89 (0x36f4) +#define AFE_CONN89_1 (0x36f8) +#define AFE_CONN89_2 (0x36fc) +#define AFE_CONN89_3 (0x3700) +#define AFE_CONN89_4 (0x3704) +#define AFE_CONN90 (0x3708) +#define AFE_CONN90_1 (0x370c) +#define AFE_CONN90_2 (0x3710) +#define AFE_CONN90_3 (0x3714) +#define AFE_CONN90_4 (0x3718) +#define AFE_CONN91 (0x371c) +#define AFE_CONN91_1 (0x3720) +#define AFE_CONN91_2 (0x3724) +#define AFE_CONN91_3 (0x3728) +#define AFE_CONN91_4 (0x372c) +#define AFE_CONN92 (0x3730) +#define AFE_CONN92_1 (0x3734) +#define AFE_CONN92_2 (0x3738) +#define AFE_CONN92_3 (0x373c) +#define AFE_CONN92_4 (0x3740) +#define AFE_CONN93 (0x3744) +#define AFE_CONN93_1 (0x3748) +#define AFE_CONN93_2 (0x374c) +#define AFE_CONN93_3 (0x3750) +#define AFE_CONN93_4 (0x3754) +#define AFE_CONN94 (0x3758) +#define AFE_CONN94_1 (0x375c) +#define AFE_CONN94_2 (0x3760) +#define AFE_CONN94_3 (0x3764) +#define AFE_CONN94_4 (0x3768) +#define AFE_CONN95 (0x376c) +#define AFE_CONN95_1 (0x3770) +#define AFE_CONN95_2 (0x3774) +#define AFE_CONN95_3 (0x3778) +#define AFE_CONN95_4 (0x377c) +#define AFE_CONN96 (0x3780) +#define AFE_CONN96_1 (0x3784) +#define AFE_CONN96_2 (0x3788) +#define AFE_CONN96_3 (0x378c) +#define AFE_CONN96_4 (0x3790) +#define AFE_CONN97 (0x3794) +#define AFE_CONN97_1 (0x3798) +#define AFE_CONN97_2 (0x379c) +#define AFE_CONN97_3 (0x37a0) +#define AFE_CONN97_4 (0x37a4) +#define AFE_CONN98 (0x37a8) +#define AFE_CONN98_1 (0x37ac) +#define AFE_CONN98_2 (0x37b0) +#define AFE_CONN98_3 (0x37b4) +#define AFE_CONN98_4 (0x37b8) +#define AFE_CONN99 (0x37bc) +#define AFE_CONN99_1 (0x37c0) +#define AFE_CONN99_2 (0x37c4) +#define AFE_CONN99_3 (0x37c8) +#define AFE_CONN99_4 (0x37cc) +#define AFE_CONN100 (0x37d0) +#define AFE_CONN100_1 (0x37d4) +#define AFE_CONN100_2 (0x37d8) +#define AFE_CONN100_3 (0x37dc) +#define AFE_CONN100_4 (0x37e0) +#define AFE_CONN101 (0x37e4) +#define AFE_CONN101_1 (0x37e8) +#define AFE_CONN101_2 (0x37ec) +#define AFE_CONN101_3 (0x37f0) +#define AFE_CONN101_4 (0x37f4) +#define AFE_CONN102 (0x37f8) +#define AFE_CONN102_1 (0x37fc) +#define AFE_CONN102_2 (0x3800) +#define AFE_CONN102_3 (0x3804) +#define AFE_CONN102_4 (0x3808) +#define AFE_CONN103 (0x380c) +#define AFE_CONN103_1 (0x3810) +#define AFE_CONN103_2 (0x3814) +#define AFE_CONN103_3 (0x3818) +#define AFE_CONN103_4 (0x381c) +#define AFE_CONN104 (0x3820) +#define AFE_CONN104_1 (0x3824) +#define AFE_CONN104_2 (0x3828) +#define AFE_CONN104_3 (0x382c) +#define AFE_CONN104_4 (0x3830) +#define AFE_CONN105 (0x3834) +#define AFE_CONN105_1 (0x3838) +#define AFE_CONN105_2 (0x383c) +#define AFE_CONN105_3 (0x3840) +#define AFE_CONN105_4 (0x3844) +#define AFE_CONN106 (0x3848) +#define AFE_CONN106_1 (0x384c) +#define AFE_CONN106_2 (0x3850) +#define AFE_CONN106_3 (0x3854) +#define AFE_CONN106_4 (0x3858) +#define AFE_CONN107 (0x385c) +#define AFE_CONN107_1 (0x3860) +#define AFE_CONN107_2 (0x3864) +#define AFE_CONN107_3 (0x3868) +#define AFE_CONN107_4 (0x386c) +#define AFE_CONN108 (0x3870) +#define AFE_CONN108_1 (0x3874) +#define AFE_CONN108_2 (0x3878) +#define AFE_CONN108_3 (0x387c) +#define AFE_CONN108_4 (0x3880) +#define AFE_CONN109 (0x3884) +#define AFE_CONN109_1 (0x3888) +#define AFE_CONN109_2 (0x388c) +#define AFE_CONN109_3 (0x3890) +#define AFE_CONN109_4 (0x3894) +#define AFE_CONN110 (0x3898) +#define AFE_CONN110_1 (0x389c) +#define AFE_CONN110_2 (0x38a0) +#define AFE_CONN110_3 (0x38a4) +#define AFE_CONN110_4 (0x38a8) +#define AFE_CONN111 (0x38ac) +#define AFE_CONN111_1 (0x38b0) +#define AFE_CONN111_2 (0x38b4) +#define AFE_CONN111_3 (0x38b8) +#define AFE_CONN111_4 (0x38bc) +#define AFE_CONN112 (0x38c0) +#define AFE_CONN112_1 (0x38c4) +#define AFE_CONN112_2 (0x38c8) +#define AFE_CONN112_3 (0x38cc) +#define AFE_CONN112_4 (0x38d0) +#define AFE_CONN113 (0x38d4) +#define AFE_CONN113_1 (0x38d8) +#define AFE_CONN113_2 (0x38dc) +#define AFE_CONN113_3 (0x38e0) +#define AFE_CONN113_4 (0x38e4) +#define AFE_CONN114 (0x38e8) +#define AFE_CONN114_1 (0x38ec) +#define AFE_CONN114_2 (0x38f0) +#define AFE_CONN114_3 (0x38f4) +#define AFE_CONN114_4 (0x38f8) +#define AFE_CONN115 (0x38fc) +#define AFE_CONN115_1 (0x3900) +#define AFE_CONN115_2 (0x3904) +#define AFE_CONN115_3 (0x3908) +#define AFE_CONN115_4 (0x390c) +#define AFE_CONN116 (0x3910) +#define AFE_CONN116_1 (0x3914) +#define AFE_CONN116_2 (0x3918) +#define AFE_CONN116_3 (0x391c) +#define AFE_CONN116_4 (0x3920) +#define AFE_CONN117 (0x3924) +#define AFE_CONN117_1 (0x3928) +#define AFE_CONN117_2 (0x392c) +#define AFE_CONN117_3 (0x3930) +#define AFE_CONN117_4 (0x3934) +#define AFE_CONN118 (0x3938) +#define AFE_CONN118_1 (0x393c) +#define AFE_CONN118_2 (0x3940) +#define AFE_CONN118_3 (0x3944) +#define AFE_CONN118_4 (0x3948) +#define AFE_CONN119 (0x394c) +#define AFE_CONN119_1 (0x3950) +#define AFE_CONN119_2 (0x3954) +#define AFE_CONN119_3 (0x3958) +#define AFE_CONN119_4 (0x395c) +#define AFE_CONN120 (0x3960) +#define AFE_CONN120_1 (0x3964) +#define AFE_CONN120_2 (0x3968) +#define AFE_CONN120_3 (0x396c) +#define AFE_CONN120_4 (0x3970) +#define AFE_CONN121 (0x3974) +#define AFE_CONN121_1 (0x3978) +#define AFE_CONN121_2 (0x397c) +#define AFE_CONN121_3 (0x3980) +#define AFE_CONN121_4 (0x3984) +#define AFE_CONN122 (0x3988) +#define AFE_CONN122_1 (0x398c) +#define AFE_CONN122_2 (0x3990) +#define AFE_CONN122_3 (0x3994) +#define AFE_CONN122_4 (0x3998) +#define AFE_CONN123 (0x399c) +#define AFE_CONN123_1 (0x39a0) +#define AFE_CONN123_2 (0x39a4) +#define AFE_CONN123_3 (0x39a8) +#define AFE_CONN123_4 (0x39ac) +#define AFE_CONN124 (0x39b0) +#define AFE_CONN124_1 (0x39b4) +#define AFE_CONN124_2 (0x39b8) +#define AFE_CONN124_3 (0x39bc) +#define AFE_CONN124_4 (0x39c0) +#define AFE_CONN125 (0x39c4) +#define AFE_CONN125_1 (0x39c8) +#define AFE_CONN125_2 (0x39cc) +#define AFE_CONN125_3 (0x39d0) +#define AFE_CONN125_4 (0x39d4) +#define AFE_CONN126 (0x39d8) +#define AFE_CONN126_1 (0x39dc) +#define AFE_CONN126_2 (0x39e0) +#define AFE_CONN126_3 (0x39e4) +#define AFE_CONN126_4 (0x39e8) +#define AFE_CONN127 (0x39ec) +#define AFE_CONN127_1 (0x39f0) +#define AFE_CONN127_2 (0x39f4) +#define AFE_CONN127_3 (0x39f8) +#define AFE_CONN127_4 (0x39fc) +#define AFE_CONN128 (0x3a00) +#define AFE_CONN128_1 (0x3a04) +#define AFE_CONN128_2 (0x3a08) +#define AFE_CONN128_3 (0x3a0c) +#define AFE_CONN128_4 (0x3a10) +#define AFE_CONN129 (0x3a14) +#define AFE_CONN129_1 (0x3a18) +#define AFE_CONN129_2 (0x3a1c) +#define AFE_CONN129_3 (0x3a20) +#define AFE_CONN129_4 (0x3a24) +#define AFE_CONN130 (0x3a28) +#define AFE_CONN130_1 (0x3a2c) +#define AFE_CONN130_2 (0x3a30) +#define AFE_CONN130_3 (0x3a34) +#define AFE_CONN130_4 (0x3a38) +#define AFE_CONN131 (0x3a3c) +#define AFE_CONN131_1 (0x3a40) +#define AFE_CONN131_2 (0x3a44) +#define AFE_CONN131_3 (0x3a48) +#define AFE_CONN131_4 (0x3a4c) +#define AFE_CONN132 (0x3a50) +#define AFE_CONN132_1 (0x3a54) +#define AFE_CONN132_2 (0x3a58) +#define AFE_CONN132_3 (0x3a5c) +#define AFE_CONN132_4 (0x3a60) +#define AFE_CONN133 (0x3a64) +#define AFE_CONN133_1 (0x3a68) +#define AFE_CONN133_2 (0x3a6c) +#define AFE_CONN133_3 (0x3a70) +#define AFE_CONN133_4 (0x3a74) +#define AFE_CONN134 (0x3a78) +#define AFE_CONN134_1 (0x3a7c) +#define AFE_CONN134_2 (0x3a80) +#define AFE_CONN134_3 (0x3a84) +#define AFE_CONN134_4 (0x3a88) +#define AFE_CONN135 (0x3a8c) +#define AFE_CONN135_1 (0x3a90) +#define AFE_CONN135_2 (0x3a94) +#define AFE_CONN135_3 (0x3a98) +#define AFE_CONN135_4 (0x3a9c) +#define AFE_CONN136 (0x3aa0) +#define AFE_CONN136_1 (0x3aa4) +#define AFE_CONN136_2 (0x3aa8) +#define AFE_CONN136_3 (0x3aac) +#define AFE_CONN136_4 (0x3ab0) +#define AFE_CONN137 (0x3ab4) +#define AFE_CONN137_1 (0x3ab8) +#define AFE_CONN137_2 (0x3abc) +#define AFE_CONN137_3 (0x3ac0) +#define AFE_CONN137_4 (0x3ac4) +#define AFE_CONN138 (0x3ac8) +#define AFE_CONN138_1 (0x3acc) +#define AFE_CONN138_2 (0x3ad0) +#define AFE_CONN138_3 (0x3ad4) +#define AFE_CONN138_4 (0x3ad8) +#define AFE_CONN139 (0x3adc) +#define AFE_CONN139_1 (0x3ae0) +#define AFE_CONN139_2 (0x3ae4) +#define AFE_CONN139_3 (0x3ae8) +#define AFE_CONN139_4 (0x3aec) +#define AFE_CONN_RS (0x3af0) +#define AFE_CONN_RS_1 (0x3af4) +#define AFE_CONN_RS_2 (0x3af8) +#define AFE_CONN_RS_3 (0x3afc) +#define AFE_CONN_RS_4 (0x3b00) +#define AFE_CONN_16BIT (0x3b04) +#define AFE_CONN_16BIT_1 (0x3b08) +#define AFE_CONN_16BIT_2 (0x3b0c) +#define AFE_CONN_16BIT_3 (0x3b10) +#define AFE_CONN_16BIT_4 (0x3b14) +#define AFE_CONN_24BIT (0x3b18) +#define AFE_CONN_24BIT_1 (0x3b1c) +#define AFE_CONN_24BIT_2 (0x3b20) +#define AFE_CONN_24BIT_3 (0x3b24) +#define AFE_CONN_24BIT_4 (0x3b28) +#define AFE_CONN_DI (0x3b2c) +#define AFE_CONN_DI_1 (0x3b30) +#define AFE_CONN_DI_2 (0x3b34) +#define AFE_CONN_DI_3 (0x3b38) +#define AFE_CONN_DI_4 (0x3b3c) +#define AFE_CONN176 (0x3ea0) +#define AFE_CONN176_1 (0x3ea4) +#define AFE_CONN176_2 (0x3ea8) +#define AFE_CONN176_3 (0x3eac) +#define AFE_CONN176_4 (0x3eb0) +#define AFE_CONN176_5 (0x3eb4) +#define AFE_CONN177 (0x3eb8) +#define AFE_CONN177_1 (0x3ebc) +#define AFE_CONN177_2 (0x3ec0) +#define AFE_CONN177_3 (0x3ec4) +#define AFE_CONN177_4 (0x3ec8) +#define AFE_CONN177_5 (0x3ecc) +#define AFE_CONN182 (0x3f30) +#define AFE_CONN182_1 (0x3f34) +#define AFE_CONN182_2 (0x3f38) +#define AFE_CONN182_3 (0x3f3c) +#define AFE_CONN182_4 (0x3f40) +#define AFE_CONN182_5 (0x3f44) +#define AFE_CONN183 (0x3f48) +#define AFE_CONN183_1 (0x3f4c) +#define AFE_CONN183_2 (0x3f50) +#define AFE_CONN183_3 (0x3f54) +#define AFE_CONN183_4 (0x3f58) +#define AFE_CONN183_5 (0x3f5c) +#define AFE_SECURE_MASK_CONN0 (0x4000) +#define AFE_SECURE_MASK_CONN0_1 (0x4004) +#define AFE_SECURE_MASK_CONN0_2 (0x4008) +#define AFE_SECURE_MASK_CONN0_3 (0x400c) +#define AFE_SECURE_MASK_CONN0_4 (0x4010) +#define AFE_SECURE_MASK_CONN1 (0x4014) +#define AFE_SECURE_MASK_CONN1_1 (0x4018) +#define AFE_SECURE_MASK_CONN1_2 (0x401c) +#define AFE_SECURE_MASK_CONN1_3 (0x4020) +#define AFE_SECURE_MASK_CONN1_4 (0x4024) +#define AFE_SECURE_MASK_CONN2 (0x4028) +#define AFE_SECURE_MASK_CONN2_1 (0x402c) +#define AFE_SECURE_MASK_CONN2_2 (0x4030) +#define AFE_SECURE_MASK_CONN2_3 (0x4034) +#define AFE_SECURE_MASK_CONN2_4 (0x4038) +#define AFE_SECURE_MASK_CONN3 (0x403c) +#define AFE_SECURE_MASK_CONN3_1 (0x4040) +#define AFE_SECURE_MASK_CONN3_2 (0x4044) +#define AFE_SECURE_MASK_CONN3_3 (0x4048) +#define AFE_SECURE_MASK_CONN3_4 (0x404c) +#define AFE_SECURE_MASK_CONN4 (0x4050) +#define AFE_SECURE_MASK_CONN4_1 (0x4054) +#define AFE_SECURE_MASK_CONN4_2 (0x4058) +#define AFE_SECURE_MASK_CONN4_3 (0x405c) +#define AFE_SECURE_MASK_CONN4_4 (0x4060) +#define AFE_SECURE_MASK_CONN5 (0x4064) +#define AFE_SECURE_MASK_CONN5_1 (0x4068) +#define AFE_SECURE_MASK_CONN5_2 (0x406c) +#define AFE_SECURE_MASK_CONN5_3 (0x4070) +#define AFE_SECURE_MASK_CONN5_4 (0x4074) +#define AFE_SECURE_MASK_CONN6 (0x4078) +#define AFE_SECURE_MASK_CONN6_1 (0x407c) +#define AFE_SECURE_MASK_CONN6_2 (0x4080) +#define AFE_SECURE_MASK_CONN6_3 (0x4084) +#define AFE_SECURE_MASK_CONN6_4 (0x4088) +#define AFE_SECURE_MASK_CONN7 (0x408c) +#define AFE_SECURE_MASK_CONN7_1 (0x4090) +#define AFE_SECURE_MASK_CONN7_2 (0x4094) +#define AFE_SECURE_MASK_CONN7_3 (0x4098) +#define AFE_SECURE_MASK_CONN7_4 (0x409c) +#define AFE_SECURE_MASK_CONN8 (0x40a0) +#define AFE_SECURE_MASK_CONN8_1 (0x40a4) +#define AFE_SECURE_MASK_CONN8_2 (0x40a8) +#define AFE_SECURE_MASK_CONN8_3 (0x40ac) +#define AFE_SECURE_MASK_CONN8_4 (0x40b0) +#define AFE_SECURE_MASK_CONN9 (0x40b4) +#define AFE_SECURE_MASK_CONN9_1 (0x40b8) +#define AFE_SECURE_MASK_CONN9_2 (0x40bc) +#define AFE_SECURE_MASK_CONN9_3 (0x40c0) +#define AFE_SECURE_MASK_CONN9_4 (0x40c4) +#define AFE_SECURE_MASK_CONN10 (0x40c8) +#define AFE_SECURE_MASK_CONN10_1 (0x40cc) +#define AFE_SECURE_MASK_CONN10_2 (0x40d0) +#define AFE_SECURE_MASK_CONN10_3 (0x40d4) +#define AFE_SECURE_MASK_CONN10_4 (0x40d8) +#define AFE_SECURE_MASK_CONN11 (0x40dc) +#define AFE_SECURE_MASK_CONN11_1 (0x40e0) +#define AFE_SECURE_MASK_CONN11_2 (0x40e4) +#define AFE_SECURE_MASK_CONN11_3 (0x40e8) +#define AFE_SECURE_MASK_CONN11_4 (0x40ec) +#define AFE_SECURE_MASK_CONN12 (0x40f0) +#define AFE_SECURE_MASK_CONN12_1 (0x40f4) +#define AFE_SECURE_MASK_CONN12_2 (0x40f8) +#define AFE_SECURE_MASK_CONN12_3 (0x40fc) +#define AFE_SECURE_MASK_CONN12_4 (0x4100) +#define AFE_SECURE_MASK_CONN13 (0x4104) +#define AFE_SECURE_MASK_CONN13_1 (0x4108) +#define AFE_SECURE_MASK_CONN13_2 (0x410c) +#define AFE_SECURE_MASK_CONN13_3 (0x4110) +#define AFE_SECURE_MASK_CONN13_4 (0x4114) +#define AFE_SECURE_MASK_CONN14 (0x4118) +#define AFE_SECURE_MASK_CONN14_1 (0x411c) +#define AFE_SECURE_MASK_CONN14_2 (0x4120) +#define AFE_SECURE_MASK_CONN14_3 (0x4124) +#define AFE_SECURE_MASK_CONN14_4 (0x4128) +#define AFE_SECURE_MASK_CONN15 (0x412c) +#define AFE_SECURE_MASK_CONN15_1 (0x4130) +#define AFE_SECURE_MASK_CONN15_2 (0x4134) +#define AFE_SECURE_MASK_CONN15_3 (0x4138) +#define AFE_SECURE_MASK_CONN15_4 (0x413c) +#define AFE_SECURE_MASK_CONN16 (0x4140) +#define AFE_SECURE_MASK_CONN16_1 (0x4144) +#define AFE_SECURE_MASK_CONN16_2 (0x4148) +#define AFE_SECURE_MASK_CONN16_3 (0x414c) +#define AFE_SECURE_MASK_CONN16_4 (0x4150) +#define AFE_SECURE_MASK_CONN17 (0x4154) +#define AFE_SECURE_MASK_CONN17_1 (0x4158) +#define AFE_SECURE_MASK_CONN17_2 (0x415c) +#define AFE_SECURE_MASK_CONN17_3 (0x4160) +#define AFE_SECURE_MASK_CONN17_4 (0x4164) +#define AFE_SECURE_MASK_CONN18 (0x4168) +#define AFE_SECURE_MASK_CONN18_1 (0x416c) +#define AFE_SECURE_MASK_CONN18_2 (0x4170) +#define AFE_SECURE_MASK_CONN18_3 (0x4174) +#define AFE_SECURE_MASK_CONN18_4 (0x4178) +#define AFE_SECURE_MASK_CONN19 (0x417c) +#define AFE_SECURE_MASK_CONN19_1 (0x4180) +#define AFE_SECURE_MASK_CONN19_2 (0x4184) +#define AFE_SECURE_MASK_CONN19_3 (0x4188) +#define AFE_SECURE_MASK_CONN19_4 (0x418c) +#define AFE_SECURE_MASK_CONN20 (0x4190) +#define AFE_SECURE_MASK_CONN20_1 (0x4194) +#define AFE_SECURE_MASK_CONN20_2 (0x4198) +#define AFE_SECURE_MASK_CONN20_3 (0x419c) +#define AFE_SECURE_MASK_CONN20_4 (0x41a0) +#define AFE_SECURE_MASK_CONN21 (0x41a4) +#define AFE_SECURE_MASK_CONN21_1 (0x41a8) +#define AFE_SECURE_MASK_CONN21_2 (0x41ac) +#define AFE_SECURE_MASK_CONN21_3 (0x41b0) +#define AFE_SECURE_MASK_CONN21_4 (0x41b4) +#define AFE_SECURE_MASK_CONN22 (0x41b8) +#define AFE_SECURE_MASK_CONN22_1 (0x41bc) +#define AFE_SECURE_MASK_CONN22_2 (0x41c0) +#define AFE_SECURE_MASK_CONN22_3 (0x41c4) +#define AFE_SECURE_MASK_CONN22_4 (0x41c8) +#define AFE_SECURE_MASK_CONN23 (0x41cc) +#define AFE_SECURE_MASK_CONN23_1 (0x41d0) +#define AFE_SECURE_MASK_CONN23_2 (0x41d4) +#define AFE_SECURE_MASK_CONN23_3 (0x41d8) +#define AFE_SECURE_MASK_CONN23_4 (0x41dc) +#define AFE_SECURE_MASK_CONN24 (0x41e0) +#define AFE_SECURE_MASK_CONN24_1 (0x41e4) +#define AFE_SECURE_MASK_CONN24_2 (0x41e8) +#define AFE_SECURE_MASK_CONN24_3 (0x41ec) +#define AFE_SECURE_MASK_CONN24_4 (0x41f0) +#define AFE_SECURE_MASK_CONN25 (0x41f4) +#define AFE_SECURE_MASK_CONN25_1 (0x41f8) +#define AFE_SECURE_MASK_CONN25_2 (0x41fc) +#define AFE_SECURE_MASK_CONN25_3 (0x4200) +#define AFE_SECURE_MASK_CONN25_4 (0x4204) +#define AFE_SECURE_MASK_CONN26 (0x4208) +#define AFE_SECURE_MASK_CONN26_1 (0x420c) +#define AFE_SECURE_MASK_CONN26_2 (0x4210) +#define AFE_SECURE_MASK_CONN26_3 (0x4214) +#define AFE_SECURE_MASK_CONN26_4 (0x4218) +#define AFE_SECURE_MASK_CONN27 (0x421c) +#define AFE_SECURE_MASK_CONN27_1 (0x4220) +#define AFE_SECURE_MASK_CONN27_2 (0x4224) +#define AFE_SECURE_MASK_CONN27_3 (0x4228) +#define AFE_SECURE_MASK_CONN27_4 (0x422c) +#define AFE_SECURE_MASK_CONN28 (0x4230) +#define AFE_SECURE_MASK_CONN28_1 (0x4234) +#define AFE_SECURE_MASK_CONN28_2 (0x4238) +#define AFE_SECURE_MASK_CONN28_3 (0x423c) +#define AFE_SECURE_MASK_CONN28_4 (0x4240) +#define AFE_SECURE_MASK_CONN29 (0x4244) +#define AFE_SECURE_MASK_CONN29_1 (0x4248) +#define AFE_SECURE_MASK_CONN29_2 (0x424c) +#define AFE_SECURE_MASK_CONN29_3 (0x4250) +#define AFE_SECURE_MASK_CONN29_4 (0x4254) +#define AFE_SECURE_MASK_CONN30 (0x4258) +#define AFE_SECURE_MASK_CONN30_1 (0x425c) +#define AFE_SECURE_MASK_CONN30_2 (0x4260) +#define AFE_SECURE_MASK_CONN30_3 (0x4264) +#define AFE_SECURE_MASK_CONN30_4 (0x4268) +#define AFE_SECURE_MASK_CONN31 (0x426c) +#define AFE_SECURE_MASK_CONN31_1 (0x4270) +#define AFE_SECURE_MASK_CONN31_2 (0x4274) +#define AFE_SECURE_MASK_CONN31_3 (0x4278) +#define AFE_SECURE_MASK_CONN31_4 (0x427c) +#define AFE_SECURE_MASK_CONN32 (0x4280) +#define AFE_SECURE_MASK_CONN32_1 (0x4284) +#define AFE_SECURE_MASK_CONN32_2 (0x4288) +#define AFE_SECURE_MASK_CONN32_3 (0x428c) +#define AFE_SECURE_MASK_CONN32_4 (0x4290) +#define AFE_SECURE_MASK_CONN33 (0x4294) +#define AFE_SECURE_MASK_CONN33_1 (0x4298) +#define AFE_SECURE_MASK_CONN33_2 (0x429c) +#define AFE_SECURE_MASK_CONN33_3 (0x42a0) +#define AFE_SECURE_MASK_CONN33_4 (0x42a4) +#define AFE_SECURE_MASK_CONN34 (0x42a8) +#define AFE_SECURE_MASK_CONN34_1 (0x42ac) +#define AFE_SECURE_MASK_CONN34_2 (0x42b0) +#define AFE_SECURE_MASK_CONN34_3 (0x42b4) +#define AFE_SECURE_MASK_CONN34_4 (0x42b8) +#define AFE_SECURE_MASK_CONN35 (0x42bc) +#define AFE_SECURE_MASK_CONN35_1 (0x42c0) +#define AFE_SECURE_MASK_CONN35_2 (0x42c4) +#define AFE_SECURE_MASK_CONN35_3 (0x42c8) +#define AFE_SECURE_MASK_CONN35_4 (0x42cc) +#define AFE_SECURE_MASK_CONN36 (0x42d0) +#define AFE_SECURE_MASK_CONN36_1 (0x42d4) +#define AFE_SECURE_MASK_CONN36_2 (0x42d8) +#define AFE_SECURE_MASK_CONN36_3 (0x42dc) +#define AFE_SECURE_MASK_CONN36_4 (0x42e0) +#define AFE_SECURE_MASK_CONN37 (0x42e4) +#define AFE_SECURE_MASK_CONN37_1 (0x42e8) +#define AFE_SECURE_MASK_CONN37_2 (0x42ec) +#define AFE_SECURE_MASK_CONN37_3 (0x42f0) +#define AFE_SECURE_MASK_CONN37_4 (0x42f4) +#define AFE_SECURE_MASK_CONN38 (0x42f8) +#define AFE_SECURE_MASK_CONN38_1 (0x42fc) +#define AFE_SECURE_MASK_CONN38_2 (0x4300) +#define AFE_SECURE_MASK_CONN38_3 (0x4304) +#define AFE_SECURE_MASK_CONN38_4 (0x4308) +#define AFE_SECURE_MASK_CONN39 (0x430c) +#define AFE_SECURE_MASK_CONN39_1 (0x4310) +#define AFE_SECURE_MASK_CONN39_2 (0x4314) +#define AFE_SECURE_MASK_CONN39_3 (0x4318) +#define AFE_SECURE_MASK_CONN39_4 (0x431c) +#define AFE_SECURE_MASK_CONN40 (0x4320) +#define AFE_SECURE_MASK_CONN40_1 (0x4324) +#define AFE_SECURE_MASK_CONN40_2 (0x4328) +#define AFE_SECURE_MASK_CONN40_3 (0x432c) +#define AFE_SECURE_MASK_CONN40_4 (0x4330) +#define AFE_SECURE_MASK_CONN41 (0x4334) +#define AFE_SECURE_MASK_CONN41_1 (0x4338) +#define AFE_SECURE_MASK_CONN41_2 (0x433c) +#define AFE_SECURE_MASK_CONN41_3 (0x4340) +#define AFE_SECURE_MASK_CONN41_4 (0x4344) +#define AFE_SECURE_MASK_CONN42 (0x4348) +#define AFE_SECURE_MASK_CONN42_1 (0x434c) +#define AFE_SECURE_MASK_CONN42_2 (0x4350) +#define AFE_SECURE_MASK_CONN42_3 (0x4354) +#define AFE_SECURE_MASK_CONN42_4 (0x4358) +#define AFE_SECURE_MASK_CONN43 (0x435c) +#define AFE_SECURE_MASK_CONN43_1 (0x4360) +#define AFE_SECURE_MASK_CONN43_2 (0x4364) +#define AFE_SECURE_MASK_CONN43_3 (0x4368) +#define AFE_SECURE_MASK_CONN43_4 (0x436c) +#define AFE_SECURE_MASK_CONN44 (0x4370) +#define AFE_SECURE_MASK_CONN44_1 (0x4374) +#define AFE_SECURE_MASK_CONN44_2 (0x4378) +#define AFE_SECURE_MASK_CONN44_3 (0x437c) +#define AFE_SECURE_MASK_CONN44_4 (0x4380) +#define AFE_SECURE_MASK_CONN45 (0x4384) +#define AFE_SECURE_MASK_CONN45_1 (0x4388) +#define AFE_SECURE_MASK_CONN45_2 (0x438c) +#define AFE_SECURE_MASK_CONN45_3 (0x4390) +#define AFE_SECURE_MASK_CONN45_4 (0x4394) +#define AFE_SECURE_MASK_CONN46 (0x4398) +#define AFE_SECURE_MASK_CONN46_1 (0x439c) +#define AFE_SECURE_MASK_CONN46_2 (0x43a0) +#define AFE_SECURE_MASK_CONN46_3 (0x43a4) +#define AFE_SECURE_MASK_CONN46_4 (0x43a8) +#define AFE_SECURE_MASK_CONN47 (0x43ac) +#define AFE_SECURE_MASK_CONN47_1 (0x43b0) +#define AFE_SECURE_MASK_CONN47_2 (0x43b4) +#define AFE_SECURE_MASK_CONN47_3 (0x43b8) +#define AFE_SECURE_MASK_CONN47_4 (0x43bc) +#define AFE_SECURE_MASK_CONN48 (0x43c0) +#define AFE_SECURE_MASK_CONN48_1 (0x43c4) +#define AFE_SECURE_MASK_CONN48_2 (0x43c8) +#define AFE_SECURE_MASK_CONN48_3 (0x43cc) +#define AFE_SECURE_MASK_CONN48_4 (0x43d0) +#define AFE_SECURE_MASK_CONN49 (0x43d4) +#define AFE_SECURE_MASK_CONN49_1 (0x43d8) +#define AFE_SECURE_MASK_CONN49_2 (0x43dc) +#define AFE_SECURE_MASK_CONN49_3 (0x43e0) +#define AFE_SECURE_MASK_CONN49_4 (0x43e4) +#define AFE_SECURE_MASK_CONN50 (0x43e8) +#define AFE_SECURE_MASK_CONN50_1 (0x43ec) +#define AFE_SECURE_MASK_CONN50_2 (0x43f0) +#define AFE_SECURE_MASK_CONN50_3 (0x43f4) +#define AFE_SECURE_MASK_CONN50_4 (0x43f8) +#define AFE_SECURE_MASK_CONN51 (0x43fc) +#define AFE_SECURE_MASK_CONN51_1 (0x4400) +#define AFE_SECURE_MASK_CONN51_2 (0x4404) +#define AFE_SECURE_MASK_CONN51_3 (0x4408) +#define AFE_SECURE_MASK_CONN51_4 (0x440c) +#define AFE_SECURE_MASK_CONN52 (0x4410) +#define AFE_SECURE_MASK_CONN52_1 (0x4414) +#define AFE_SECURE_MASK_CONN52_2 (0x4418) +#define AFE_SECURE_MASK_CONN52_3 (0x441c) +#define AFE_SECURE_MASK_CONN52_4 (0x4420) +#define AFE_SECURE_MASK_CONN53 (0x4424) +#define AFE_SECURE_MASK_CONN53_1 (0x4428) +#define AFE_SECURE_MASK_CONN53_2 (0x442c) +#define AFE_SECURE_MASK_CONN53_3 (0x4430) +#define AFE_SECURE_MASK_CONN53_4 (0x4434) +#define AFE_SECURE_MASK_CONN54 (0x4438) +#define AFE_SECURE_MASK_CONN54_1 (0x443c) +#define AFE_SECURE_MASK_CONN54_2 (0x4440) +#define AFE_SECURE_MASK_CONN54_3 (0x4444) +#define AFE_SECURE_MASK_CONN54_4 (0x4448) +#define AFE_SECURE_MASK_CONN55 (0x444c) +#define AFE_SECURE_MASK_CONN55_1 (0x4450) +#define AFE_SECURE_MASK_CONN55_2 (0x4454) +#define AFE_SECURE_MASK_CONN55_3 (0x4458) +#define AFE_SECURE_MASK_CONN55_4 (0x445c) +#define AFE_SECURE_MASK_CONN56 (0x4460) +#define AFE_SECURE_MASK_CONN56_1 (0x4464) +#define AFE_SECURE_MASK_CONN56_2 (0x4468) +#define AFE_SECURE_MASK_CONN56_3 (0x446c) +#define AFE_SECURE_MASK_CONN56_4 (0x4470) +#define AFE_SECURE_MASK_CONN57 (0x4474) +#define AFE_SECURE_MASK_CONN57_1 (0x4478) +#define AFE_SECURE_MASK_CONN57_2 (0x447c) +#define AFE_SECURE_MASK_CONN57_3 (0x4480) +#define AFE_SECURE_MASK_CONN57_4 (0x4484) +#define AFE_SECURE_MASK_CONN58 (0x4488) +#define AFE_SECURE_MASK_CONN58_1 (0x448c) +#define AFE_SECURE_MASK_CONN58_2 (0x4490) +#define AFE_SECURE_MASK_CONN58_3 (0x4494) +#define AFE_SECURE_MASK_CONN58_4 (0x4498) +#define AFE_SECURE_MASK_CONN59 (0x449c) +#define AFE_SECURE_MASK_CONN59_1 (0x44a0) +#define AFE_SECURE_MASK_CONN59_2 (0x44a4) +#define AFE_SECURE_MASK_CONN59_3 (0x44a8) +#define AFE_SECURE_MASK_CONN59_4 (0x44ac) +#define AFE_SECURE_MASK_CONN60 (0x44b0) +#define AFE_SECURE_MASK_CONN60_1 (0x44b4) +#define AFE_SECURE_MASK_CONN60_2 (0x44b8) +#define AFE_SECURE_MASK_CONN60_3 (0x44bc) +#define AFE_SECURE_MASK_CONN60_4 (0x44c0) +#define AFE_SECURE_MASK_CONN61 (0x44c4) +#define AFE_SECURE_MASK_CONN61_1 (0x44c8) +#define AFE_SECURE_MASK_CONN61_2 (0x44cc) +#define AFE_SECURE_MASK_CONN61_3 (0x44d0) +#define AFE_SECURE_MASK_CONN61_4 (0x44d4) +#define AFE_SECURE_MASK_CONN62 (0x44d8) +#define AFE_SECURE_MASK_CONN62_1 (0x44dc) +#define AFE_SECURE_MASK_CONN62_2 (0x44e0) +#define AFE_SECURE_MASK_CONN62_3 (0x44e4) +#define AFE_SECURE_MASK_CONN62_4 (0x44e8) +#define AFE_SECURE_MASK_CONN63 (0x44ec) +#define AFE_SECURE_MASK_CONN63_1 (0x44f0) +#define AFE_SECURE_MASK_CONN63_2 (0x44f4) +#define AFE_SECURE_MASK_CONN63_3 (0x44f8) +#define AFE_SECURE_MASK_CONN63_4 (0x44fc) +#define AFE_SECURE_MASK_CONN64 (0x4500) +#define AFE_SECURE_MASK_CONN64_1 (0x4504) +#define AFE_SECURE_MASK_CONN64_2 (0x4508) +#define AFE_SECURE_MASK_CONN64_3 (0x450c) +#define AFE_SECURE_MASK_CONN64_4 (0x4510) +#define AFE_SECURE_MASK_CONN65 (0x4514) +#define AFE_SECURE_MASK_CONN65_1 (0x4518) +#define AFE_SECURE_MASK_CONN65_2 (0x451c) +#define AFE_SECURE_MASK_CONN65_3 (0x4520) +#define AFE_SECURE_MASK_CONN65_4 (0x4524) +#define AFE_SECURE_MASK_CONN66 (0x4528) +#define AFE_SECURE_MASK_CONN66_1 (0x452c) +#define AFE_SECURE_MASK_CONN66_2 (0x4530) +#define AFE_SECURE_MASK_CONN66_3 (0x4534) +#define AFE_SECURE_MASK_CONN66_4 (0x4538) +#define AFE_SECURE_MASK_CONN67 (0x453c) +#define AFE_SECURE_MASK_CONN67_1 (0x4540) +#define AFE_SECURE_MASK_CONN67_2 (0x4544) +#define AFE_SECURE_MASK_CONN67_3 (0x4548) +#define AFE_SECURE_MASK_CONN67_4 (0x454c) +#define AFE_SECURE_MASK_CONN68 (0x4550) +#define AFE_SECURE_MASK_CONN68_1 (0x4554) +#define AFE_SECURE_MASK_CONN68_2 (0x4558) +#define AFE_SECURE_MASK_CONN68_3 (0x455c) +#define AFE_SECURE_MASK_CONN68_4 (0x4560) +#define AFE_SECURE_MASK_CONN69 (0x4564) +#define AFE_SECURE_MASK_CONN69_1 (0x4568) +#define AFE_SECURE_MASK_CONN69_2 (0x456c) +#define AFE_SECURE_MASK_CONN69_3 (0x4570) +#define AFE_SECURE_MASK_CONN69_4 (0x4574) +#define AFE_SECURE_MASK_CONN70 (0x4578) +#define AFE_SECURE_MASK_CONN70_1 (0x457c) +#define AFE_SECURE_MASK_CONN70_2 (0x4580) +#define AFE_SECURE_MASK_CONN70_3 (0x4584) +#define AFE_SECURE_MASK_CONN70_4 (0x4588) +#define AFE_SECURE_MASK_CONN71 (0x458c) +#define AFE_SECURE_MASK_CONN71_1 (0x4590) +#define AFE_SECURE_MASK_CONN71_2 (0x4594) +#define AFE_SECURE_MASK_CONN71_3 (0x4598) +#define AFE_SECURE_MASK_CONN71_4 (0x459c) +#define AFE_SECURE_MASK_CONN72 (0x45a0) +#define AFE_SECURE_MASK_CONN72_1 (0x45a4) +#define AFE_SECURE_MASK_CONN72_2 (0x45a8) +#define AFE_SECURE_MASK_CONN72_3 (0x45ac) +#define AFE_SECURE_MASK_CONN72_4 (0x45b0) +#define AFE_SECURE_MASK_CONN73 (0x45b4) +#define AFE_SECURE_MASK_CONN73_1 (0x45b8) +#define AFE_SECURE_MASK_CONN73_2 (0x45bc) +#define AFE_SECURE_MASK_CONN73_3 (0x45c0) +#define AFE_SECURE_MASK_CONN73_4 (0x45c4) +#define AFE_SECURE_MASK_CONN74 (0x45c8) +#define AFE_SECURE_MASK_CONN74_1 (0x45cc) +#define AFE_SECURE_MASK_CONN74_2 (0x45d0) +#define AFE_SECURE_MASK_CONN74_3 (0x45d4) +#define AFE_SECURE_MASK_CONN74_4 (0x45d8) +#define AFE_SECURE_MASK_CONN75 (0x45dc) +#define AFE_SECURE_MASK_CONN75_1 (0x45e0) +#define AFE_SECURE_MASK_CONN75_2 (0x45e4) +#define AFE_SECURE_MASK_CONN75_3 (0x45e8) +#define AFE_SECURE_MASK_CONN75_4 (0x45ec) +#define AFE_SECURE_MASK_CONN76 (0x45f0) +#define AFE_SECURE_MASK_CONN76_1 (0x45f4) +#define AFE_SECURE_MASK_CONN76_2 (0x45f8) +#define AFE_SECURE_MASK_CONN76_3 (0x45fc) +#define AFE_SECURE_MASK_CONN76_4 (0x4600) +#define AFE_SECURE_MASK_CONN77 (0x4604) +#define AFE_SECURE_MASK_CONN77_1 (0x4608) +#define AFE_SECURE_MASK_CONN77_2 (0x460c) +#define AFE_SECURE_MASK_CONN77_3 (0x4610) +#define AFE_SECURE_MASK_CONN77_4 (0x4614) +#define AFE_SECURE_MASK_CONN78 (0x4618) +#define AFE_SECURE_MASK_CONN78_1 (0x461c) +#define AFE_SECURE_MASK_CONN78_2 (0x4620) +#define AFE_SECURE_MASK_CONN78_3 (0x4624) +#define AFE_SECURE_MASK_CONN78_4 (0x4628) +#define AFE_SECURE_MASK_CONN79 (0x462c) +#define AFE_SECURE_MASK_CONN79_1 (0x4630) +#define AFE_SECURE_MASK_CONN79_2 (0x4634) +#define AFE_SECURE_MASK_CONN79_3 (0x4638) +#define AFE_SECURE_MASK_CONN79_4 (0x463c) +#define AFE_SECURE_MASK_CONN80 (0x4640) +#define AFE_SECURE_MASK_CONN80_1 (0x4644) +#define AFE_SECURE_MASK_CONN80_2 (0x4648) +#define AFE_SECURE_MASK_CONN80_3 (0x464c) +#define AFE_SECURE_MASK_CONN80_4 (0x4650) +#define AFE_SECURE_MASK_CONN81 (0x4654) +#define AFE_SECURE_MASK_CONN81_1 (0x4658) +#define AFE_SECURE_MASK_CONN81_2 (0x465c) +#define AFE_SECURE_MASK_CONN81_3 (0x4660) +#define AFE_SECURE_MASK_CONN81_4 (0x4664) +#define AFE_SECURE_MASK_CONN82 (0x4668) +#define AFE_SECURE_MASK_CONN82_1 (0x466c) +#define AFE_SECURE_MASK_CONN82_2 (0x4670) +#define AFE_SECURE_MASK_CONN82_3 (0x4674) +#define AFE_SECURE_MASK_CONN82_4 (0x4678) +#define AFE_SECURE_MASK_CONN83 (0x467c) +#define AFE_SECURE_MASK_CONN83_1 (0x4680) +#define AFE_SECURE_MASK_CONN83_2 (0x4684) +#define AFE_SECURE_MASK_CONN83_3 (0x4688) +#define AFE_SECURE_MASK_CONN83_4 (0x468c) +#define AFE_SECURE_MASK_CONN84 (0x4690) +#define AFE_SECURE_MASK_CONN84_1 (0x4694) +#define AFE_SECURE_MASK_CONN84_2 (0x4698) +#define AFE_SECURE_MASK_CONN84_3 (0x469c) +#define AFE_SECURE_MASK_CONN84_4 (0x46a0) +#define AFE_SECURE_MASK_CONN85 (0x46a4) +#define AFE_SECURE_MASK_CONN85_1 (0x46a8) +#define AFE_SECURE_MASK_CONN85_2 (0x46ac) +#define AFE_SECURE_MASK_CONN85_3 (0x46b0) +#define AFE_SECURE_MASK_CONN85_4 (0x46b4) +#define AFE_SECURE_MASK_CONN86 (0x46b8) +#define AFE_SECURE_MASK_CONN86_1 (0x46bc) +#define AFE_SECURE_MASK_CONN86_2 (0x46c0) +#define AFE_SECURE_MASK_CONN86_3 (0x46c4) +#define AFE_SECURE_MASK_CONN86_4 (0x46c8) +#define AFE_SECURE_MASK_CONN87 (0x46cc) +#define AFE_SECURE_MASK_CONN87_1 (0x46d0) +#define AFE_SECURE_MASK_CONN87_2 (0x46d4) +#define AFE_SECURE_MASK_CONN87_3 (0x46d8) +#define AFE_SECURE_MASK_CONN87_4 (0x46dc) +#define AFE_SECURE_MASK_CONN88 (0x46e0) +#define AFE_SECURE_MASK_CONN88_1 (0x46e4) +#define AFE_SECURE_MASK_CONN88_2 (0x46e8) +#define AFE_SECURE_MASK_CONN88_3 (0x46ec) +#define AFE_SECURE_MASK_CONN88_4 (0x46f0) +#define AFE_SECURE_MASK_CONN89 (0x46f4) +#define AFE_SECURE_MASK_CONN89_1 (0x46f8) +#define AFE_SECURE_MASK_CONN89_2 (0x46fc) +#define AFE_SECURE_MASK_CONN89_3 (0x4700) +#define AFE_SECURE_MASK_CONN89_4 (0x4704) +#define AFE_SECURE_MASK_CONN90 (0x4708) +#define AFE_SECURE_MASK_CONN90_1 (0x470c) +#define AFE_SECURE_MASK_CONN90_2 (0x4710) +#define AFE_SECURE_MASK_CONN90_3 (0x4714) +#define AFE_SECURE_MASK_CONN90_4 (0x4718) +#define AFE_SECURE_MASK_CONN91 (0x471c) +#define AFE_SECURE_MASK_CONN91_1 (0x4720) +#define AFE_SECURE_MASK_CONN91_2 (0x4724) +#define AFE_SECURE_MASK_CONN91_3 (0x4728) +#define AFE_SECURE_MASK_CONN91_4 (0x472c) +#define AFE_SECURE_MASK_CONN92 (0x4730) +#define AFE_SECURE_MASK_CONN92_1 (0x4734) +#define AFE_SECURE_MASK_CONN92_2 (0x4738) +#define AFE_SECURE_MASK_CONN92_3 (0x473c) +#define AFE_SECURE_MASK_CONN92_4 (0x4740) +#define AFE_SECURE_MASK_CONN93 (0x4744) +#define AFE_SECURE_MASK_CONN93_1 (0x4748) +#define AFE_SECURE_MASK_CONN93_2 (0x474c) +#define AFE_SECURE_MASK_CONN93_3 (0x4750) +#define AFE_SECURE_MASK_CONN93_4 (0x4754) +#define AFE_SECURE_MASK_CONN94 (0x4758) +#define AFE_SECURE_MASK_CONN94_1 (0x475c) +#define AFE_SECURE_MASK_CONN94_2 (0x4760) +#define AFE_SECURE_MASK_CONN94_3 (0x4764) +#define AFE_SECURE_MASK_CONN94_4 (0x4768) +#define AFE_SECURE_MASK_CONN95 (0x476c) +#define AFE_SECURE_MASK_CONN95_1 (0x4770) +#define AFE_SECURE_MASK_CONN95_2 (0x4774) +#define AFE_SECURE_MASK_CONN95_3 (0x4778) +#define AFE_SECURE_MASK_CONN95_4 (0x477c) +#define AFE_SECURE_MASK_CONN96 (0x4780) +#define AFE_SECURE_MASK_CONN96_1 (0x4784) +#define AFE_SECURE_MASK_CONN96_2 (0x4788) +#define AFE_SECURE_MASK_CONN96_3 (0x478c) +#define AFE_SECURE_MASK_CONN96_4 (0x4790) +#define AFE_SECURE_MASK_CONN97 (0x4794) +#define AFE_SECURE_MASK_CONN97_1 (0x4798) +#define AFE_SECURE_MASK_CONN97_2 (0x479c) +#define AFE_SECURE_MASK_CONN97_3 (0x47a0) +#define AFE_SECURE_MASK_CONN97_4 (0x47a4) +#define AFE_SECURE_MASK_CONN98 (0x47a8) +#define AFE_SECURE_MASK_CONN98_1 (0x47ac) +#define AFE_SECURE_MASK_CONN98_2 (0x47b0) +#define AFE_SECURE_MASK_CONN98_3 (0x47b4) +#define AFE_SECURE_MASK_CONN98_4 (0x47b8) +#define AFE_SECURE_MASK_CONN99 (0x47bc) +#define AFE_SECURE_MASK_CONN99_1 (0x47c0) +#define AFE_SECURE_MASK_CONN99_2 (0x47c4) +#define AFE_SECURE_MASK_CONN99_3 (0x47c8) +#define AFE_SECURE_MASK_CONN99_4 (0x47cc) +#define AFE_SECURE_MASK_CONN100 (0x47d0) +#define AFE_SECURE_MASK_CONN100_1 (0x47d4) +#define AFE_SECURE_MASK_CONN100_2 (0x47d8) +#define AFE_SECURE_MASK_CONN100_3 (0x47dc) +#define AFE_SECURE_MASK_CONN100_4 (0x47e0) +#define AFE_SECURE_MASK_CONN101 (0x47e4) +#define AFE_SECURE_MASK_CONN101_1 (0x47e8) +#define AFE_SECURE_MASK_CONN101_2 (0x47ec) +#define AFE_SECURE_MASK_CONN101_3 (0x47f0) +#define AFE_SECURE_MASK_CONN101_4 (0x47f4) +#define AFE_SECURE_MASK_CONN102 (0x47f8) +#define AFE_SECURE_MASK_CONN102_1 (0x47fc) +#define AFE_SECURE_MASK_CONN102_2 (0x4800) +#define AFE_SECURE_MASK_CONN102_3 (0x4804) +#define AFE_SECURE_MASK_CONN102_4 (0x4808) +#define AFE_SECURE_MASK_CONN103 (0x480c) +#define AFE_SECURE_MASK_CONN103_1 (0x4810) +#define AFE_SECURE_MASK_CONN103_2 (0x4814) +#define AFE_SECURE_MASK_CONN103_3 (0x4818) +#define AFE_SECURE_MASK_CONN103_4 (0x481c) +#define AFE_SECURE_MASK_CONN104 (0x4820) +#define AFE_SECURE_MASK_CONN104_1 (0x4824) +#define AFE_SECURE_MASK_CONN104_2 (0x4828) +#define AFE_SECURE_MASK_CONN104_3 (0x482c) +#define AFE_SECURE_MASK_CONN104_4 (0x4830) +#define AFE_SECURE_MASK_CONN105 (0x4834) +#define AFE_SECURE_MASK_CONN105_1 (0x4838) +#define AFE_SECURE_MASK_CONN105_2 (0x483c) +#define AFE_SECURE_MASK_CONN105_3 (0x4840) +#define AFE_SECURE_MASK_CONN105_4 (0x4844) +#define AFE_SECURE_MASK_CONN106 (0x4848) +#define AFE_SECURE_MASK_CONN106_1 (0x484c) +#define AFE_SECURE_MASK_CONN106_2 (0x4850) +#define AFE_SECURE_MASK_CONN106_3 (0x4854) +#define AFE_SECURE_MASK_CONN106_4 (0x4858) +#define AFE_SECURE_MASK_CONN107 (0x485c) +#define AFE_SECURE_MASK_CONN107_1 (0x4860) +#define AFE_SECURE_MASK_CONN107_2 (0x4864) +#define AFE_SECURE_MASK_CONN107_3 (0x4868) +#define AFE_SECURE_MASK_CONN107_4 (0x486c) +#define AFE_SECURE_MASK_CONN108 (0x4870) +#define AFE_SECURE_MASK_CONN108_1 (0x4874) +#define AFE_SECURE_MASK_CONN108_2 (0x4878) +#define AFE_SECURE_MASK_CONN108_3 (0x487c) +#define AFE_SECURE_MASK_CONN108_4 (0x4880) +#define AFE_SECURE_MASK_CONN109 (0x4884) +#define AFE_SECURE_MASK_CONN109_1 (0x4888) +#define AFE_SECURE_MASK_CONN109_2 (0x488c) +#define AFE_SECURE_MASK_CONN109_3 (0x4890) +#define AFE_SECURE_MASK_CONN109_4 (0x4894) +#define AFE_SECURE_MASK_CONN110 (0x4898) +#define AFE_SECURE_MASK_CONN110_1 (0x489c) +#define AFE_SECURE_MASK_CONN110_2 (0x48a0) +#define AFE_SECURE_MASK_CONN110_3 (0x48a4) +#define AFE_SECURE_MASK_CONN110_4 (0x48a8) +#define AFE_SECURE_MASK_CONN111 (0x48ac) +#define AFE_SECURE_MASK_CONN111_1 (0x48b0) +#define AFE_SECURE_MASK_CONN111_2 (0x48b4) +#define AFE_SECURE_MASK_CONN111_3 (0x48b8) +#define AFE_SECURE_MASK_CONN111_4 (0x48bc) +#define AFE_SECURE_MASK_CONN112 (0x48c0) +#define AFE_SECURE_MASK_CONN112_1 (0x48c4) +#define AFE_SECURE_MASK_CONN112_2 (0x48c8) +#define AFE_SECURE_MASK_CONN112_3 (0x48cc) +#define AFE_SECURE_MASK_CONN112_4 (0x48d0) +#define AFE_SECURE_MASK_CONN113 (0x48d4) +#define AFE_SECURE_MASK_CONN113_1 (0x48d8) +#define AFE_SECURE_MASK_CONN113_2 (0x48dc) +#define AFE_SECURE_MASK_CONN113_3 (0x48e0) +#define AFE_SECURE_MASK_CONN113_4 (0x48e4) +#define AFE_SECURE_MASK_CONN114 (0x48e8) +#define AFE_SECURE_MASK_CONN114_1 (0x48ec) +#define AFE_SECURE_MASK_CONN114_2 (0x48f0) +#define AFE_SECURE_MASK_CONN114_3 (0x48f4) +#define AFE_SECURE_MASK_CONN114_4 (0x48f8) +#define AFE_SECURE_MASK_CONN115 (0x48fc) +#define AFE_SECURE_MASK_CONN115_1 (0x4900) +#define AFE_SECURE_MASK_CONN115_2 (0x4904) +#define AFE_SECURE_MASK_CONN115_3 (0x4908) +#define AFE_SECURE_MASK_CONN115_4 (0x490c) +#define AFE_SECURE_MASK_CONN116 (0x4910) +#define AFE_SECURE_MASK_CONN116_1 (0x4914) +#define AFE_SECURE_MASK_CONN116_2 (0x4918) +#define AFE_SECURE_MASK_CONN116_3 (0x491c) +#define AFE_SECURE_MASK_CONN116_4 (0x4920) +#define AFE_SECURE_MASK_CONN117 (0x4924) +#define AFE_SECURE_MASK_CONN117_1 (0x4928) +#define AFE_SECURE_MASK_CONN117_2 (0x492c) +#define AFE_SECURE_MASK_CONN117_3 (0x4930) +#define AFE_SECURE_MASK_CONN117_4 (0x4934) +#define AFE_SECURE_MASK_CONN118 (0x4938) +#define AFE_SECURE_MASK_CONN118_1 (0x493c) +#define AFE_SECURE_MASK_CONN118_2 (0x4940) +#define AFE_SECURE_MASK_CONN118_3 (0x4944) +#define AFE_SECURE_MASK_CONN118_4 (0x4948) +#define AFE_SECURE_MASK_CONN119 (0x494c) +#define AFE_SECURE_MASK_CONN119_1 (0x4950) +#define AFE_SECURE_MASK_CONN119_2 (0x4954) +#define AFE_SECURE_MASK_CONN119_3 (0x4958) +#define AFE_SECURE_MASK_CONN119_4 (0x495c) +#define AFE_SECURE_MASK_CONN120 (0x4960) +#define AFE_SECURE_MASK_CONN120_1 (0x4964) +#define AFE_SECURE_MASK_CONN120_2 (0x4968) +#define AFE_SECURE_MASK_CONN120_3 (0x496c) +#define AFE_SECURE_MASK_CONN120_4 (0x4970) +#define AFE_SECURE_MASK_CONN121 (0x4974) +#define AFE_SECURE_MASK_CONN121_1 (0x4978) +#define AFE_SECURE_MASK_CONN121_2 (0x497c) +#define AFE_SECURE_MASK_CONN121_3 (0x4980) +#define AFE_SECURE_MASK_CONN121_4 (0x4984) +#define AFE_SECURE_MASK_CONN122 (0x4988) +#define AFE_SECURE_MASK_CONN122_1 (0x498c) +#define AFE_SECURE_MASK_CONN122_2 (0x4990) +#define AFE_SECURE_MASK_CONN122_3 (0x4994) +#define AFE_SECURE_MASK_CONN122_4 (0x4998) +#define AFE_SECURE_MASK_CONN123 (0x499c) +#define AFE_SECURE_MASK_CONN123_1 (0x49a0) +#define AFE_SECURE_MASK_CONN123_2 (0x49a4) +#define AFE_SECURE_MASK_CONN123_3 (0x49a8) +#define AFE_SECURE_MASK_CONN123_4 (0x49ac) +#define AFE_SECURE_MASK_CONN124 (0x49b0) +#define AFE_SECURE_MASK_CONN124_1 (0x49b4) +#define AFE_SECURE_MASK_CONN124_2 (0x49b8) +#define AFE_SECURE_MASK_CONN124_3 (0x49bc) +#define AFE_SECURE_MASK_CONN124_4 (0x49c0) +#define AFE_SECURE_MASK_CONN125 (0x49c4) +#define AFE_SECURE_MASK_CONN125_1 (0x49c8) +#define AFE_SECURE_MASK_CONN125_2 (0x49cc) +#define AFE_SECURE_MASK_CONN125_3 (0x49d0) +#define AFE_SECURE_MASK_CONN125_4 (0x49d4) +#define AFE_SECURE_MASK_CONN126 (0x49d8) +#define AFE_SECURE_MASK_CONN126_1 (0x49dc) +#define AFE_SECURE_MASK_CONN126_2 (0x49e0) +#define AFE_SECURE_MASK_CONN126_3 (0x49e4) +#define AFE_SECURE_MASK_CONN126_4 (0x49e8) +#define AFE_SECURE_MASK_CONN127 (0x49ec) +#define AFE_SECURE_MASK_CONN127_1 (0x49f0) +#define AFE_SECURE_MASK_CONN127_2 (0x49f4) +#define AFE_SECURE_MASK_CONN127_3 (0x49f8) +#define AFE_SECURE_MASK_CONN127_4 (0x49fc) +#define AFE_SECURE_MASK_CONN128 (0x4a00) +#define AFE_SECURE_MASK_CONN128_1 (0x4a04) +#define AFE_SECURE_MASK_CONN128_2 (0x4a08) +#define AFE_SECURE_MASK_CONN128_3 (0x4a0c) +#define AFE_SECURE_MASK_CONN128_4 (0x4a10) +#define AFE_SECURE_MASK_CONN129 (0x4a14) +#define AFE_SECURE_MASK_CONN129_1 (0x4a18) +#define AFE_SECURE_MASK_CONN129_2 (0x4a1c) +#define AFE_SECURE_MASK_CONN129_3 (0x4a20) +#define AFE_SECURE_MASK_CONN129_4 (0x4a24) +#define AFE_SECURE_MASK_CONN130 (0x4a28) +#define AFE_SECURE_MASK_CONN130_1 (0x4a2c) +#define AFE_SECURE_MASK_CONN130_2 (0x4a30) +#define AFE_SECURE_MASK_CONN130_3 (0x4a34) +#define AFE_SECURE_MASK_CONN130_4 (0x4a38) +#define AFE_SECURE_MASK_CONN131 (0x4a3c) +#define AFE_SECURE_MASK_CONN131_1 (0x4a40) +#define AFE_SECURE_MASK_CONN131_2 (0x4a44) +#define AFE_SECURE_MASK_CONN131_3 (0x4a48) +#define AFE_SECURE_MASK_CONN131_4 (0x4a4c) +#define AFE_SECURE_MASK_CONN132 (0x4a50) +#define AFE_SECURE_MASK_CONN132_1 (0x4a54) +#define AFE_SECURE_MASK_CONN132_2 (0x4a58) +#define AFE_SECURE_MASK_CONN132_3 (0x4a5c) +#define AFE_SECURE_MASK_CONN132_4 (0x4a60) +#define AFE_SECURE_MASK_CONN133 (0x4a64) +#define AFE_SECURE_MASK_CONN133_1 (0x4a68) +#define AFE_SECURE_MASK_CONN133_2 (0x4a6c) +#define AFE_SECURE_MASK_CONN133_3 (0x4a70) +#define AFE_SECURE_MASK_CONN133_4 (0x4a74) +#define AFE_SECURE_MASK_CONN134 (0x4a78) +#define AFE_SECURE_MASK_CONN134_1 (0x4a7c) +#define AFE_SECURE_MASK_CONN134_2 (0x4a80) +#define AFE_SECURE_MASK_CONN134_3 (0x4a84) +#define AFE_SECURE_MASK_CONN134_4 (0x4a88) +#define AFE_SECURE_MASK_CONN135 (0x4a8c) +#define AFE_SECURE_MASK_CONN135_1 (0x4a90) +#define AFE_SECURE_MASK_CONN135_2 (0x4a94) +#define AFE_SECURE_MASK_CONN135_3 (0x4a98) +#define AFE_SECURE_MASK_CONN135_4 (0x4a9c) +#define AFE_SECURE_MASK_CONN136 (0x4aa0) +#define AFE_SECURE_MASK_CONN136_1 (0x4aa4) +#define AFE_SECURE_MASK_CONN136_2 (0x4aa8) +#define AFE_SECURE_MASK_CONN136_3 (0x4aac) +#define AFE_SECURE_MASK_CONN136_4 (0x4ab0) +#define AFE_SECURE_MASK_CONN137 (0x4ab4) +#define AFE_SECURE_MASK_CONN137_1 (0x4ab8) +#define AFE_SECURE_MASK_CONN137_2 (0x4abc) +#define AFE_SECURE_MASK_CONN137_3 (0x4ac0) +#define AFE_SECURE_MASK_CONN137_4 (0x4ac4) +#define AFE_SECURE_MASK_CONN138 (0x4ac8) +#define AFE_SECURE_MASK_CONN138_1 (0x4acc) +#define AFE_SECURE_MASK_CONN138_2 (0x4ad0) +#define AFE_SECURE_MASK_CONN138_3 (0x4ad4) +#define AFE_SECURE_MASK_CONN138_4 (0x4ad8) +#define AFE_SECURE_MASK_CONN139 (0x4adc) +#define AFE_SECURE_MASK_CONN139_1 (0x4ae0) +#define AFE_SECURE_MASK_CONN139_2 (0x4ae4) +#define AFE_SECURE_MASK_CONN139_3 (0x4ae8) +#define AFE_SECURE_MASK_CONN139_4 (0x4aec) +#define AFE_SECURE_MASK_CONN_RS (0x4af0) +#define AFE_SECURE_MASK_CONN_RS_1 (0x4af4) +#define AFE_SECURE_MASK_CONN_RS_2 (0x4af8) +#define AFE_SECURE_MASK_CONN_RS_3 (0x4afc) +#define AFE_SECURE_MASK_CONN_RS_4 (0x4b00) +#define AFE_SECURE_MASK_CONN_16BIT (0x4b04) +#define AFE_SECURE_MASK_CONN_16BIT_1 (0x4b08) +#define AFE_SECURE_MASK_CONN_16BIT_2 (0x4b0c) +#define AFE_SECURE_MASK_CONN_16BIT_3 (0x4b10) +#define AFE_SECURE_MASK_CONN_16BIT_4 (0x4b14) +#define AFE_SECURE_MASK_CONN_24BIT (0x4b18) +#define AFE_SECURE_MASK_CONN_24BIT_1 (0x4b1c) +#define AFE_SECURE_MASK_CONN_24BIT_2 (0x4b20) +#define AFE_SECURE_MASK_CONN_24BIT_3 (0x4b24) +#define AFE_SECURE_MASK_CONN_24BIT_4 (0x4b28) +#define AFE_SECURE_MASK_CONN0_5 (0x4b2c) +#define AFE_SECURE_MASK_CONN1_5 (0x4b30) +#define AFE_SECURE_MASK_CONN2_5 (0x4b34) +#define AFE_SECURE_MASK_CONN3_5 (0x4b38) +#define AFE_SECURE_MASK_CONN4_5 (0x4b3c) +#define AFE_SECURE_MASK_CONN5_5 (0x4b40) +#define AFE_SECURE_MASK_CONN6_5 (0x4b44) +#define AFE_SECURE_MASK_CONN7_5 (0x4b48) +#define AFE_SECURE_MASK_CONN8_5 (0x4b4c) +#define AFE_SECURE_MASK_CONN9_5 (0x4b50) +#define AFE_SECURE_MASK_CONN10_5 (0x4b54) +#define AFE_SECURE_MASK_CONN11_5 (0x4b58) +#define AFE_SECURE_MASK_CONN12_5 (0x4b5c) +#define AFE_SECURE_MASK_CONN13_5 (0x4b60) +#define AFE_SECURE_MASK_CONN14_5 (0x4b64) +#define AFE_SECURE_MASK_CONN15_5 (0x4b68) +#define AFE_SECURE_MASK_CONN16_5 (0x4b6c) +#define AFE_SECURE_MASK_CONN17_5 (0x4b70) +#define AFE_SECURE_MASK_CONN18_5 (0x4b74) +#define AFE_SECURE_MASK_CONN19_5 (0x4b78) +#define AFE_SECURE_MASK_CONN20_5 (0x4b7c) +#define AFE_SECURE_MASK_CONN21_5 (0x4b80) +#define AFE_SECURE_MASK_CONN22_5 (0x4b84) +#define AFE_SECURE_MASK_CONN23_5 (0x4b88) +#define AFE_SECURE_MASK_CONN24_5 (0x4b8c) +#define AFE_SECURE_MASK_CONN25_5 (0x4b90) +#define AFE_SECURE_MASK_CONN26_5 (0x4b94) +#define AFE_SECURE_MASK_CONN27_5 (0x4b98) +#define AFE_SECURE_MASK_CONN28_5 (0x4b9c) +#define AFE_SECURE_MASK_CONN29_5 (0x4ba0) +#define AFE_SECURE_MASK_CONN30_5 (0x4ba4) +#define AFE_SECURE_MASK_CONN31_5 (0x4ba8) +#define AFE_SECURE_MASK_CONN32_5 (0x4bac) +#define AFE_SECURE_MASK_CONN33_5 (0x4bb0) +#define AFE_SECURE_MASK_CONN34_5 (0x4bb4) +#define AFE_SECURE_MASK_CONN35_5 (0x4bb8) +#define AFE_SECURE_MASK_CONN36_5 (0x4bbc) +#define AFE_SECURE_MASK_CONN37_5 (0x4bc0) +#define AFE_SECURE_MASK_CONN38_5 (0x4bc4) +#define AFE_SECURE_MASK_CONN39_5 (0x4bc8) +#define AFE_SECURE_MASK_CONN40_5 (0x4bcc) +#define AFE_SECURE_MASK_CONN41_5 (0x4bd0) +#define AFE_SECURE_MASK_CONN42_5 (0x4bd4) +#define AFE_SECURE_MASK_CONN43_5 (0x4bd8) +#define AFE_SECURE_MASK_CONN44_5 (0x4bdc) +#define AFE_SECURE_MASK_CONN45_5 (0x4be0) +#define AFE_SECURE_MASK_CONN46_5 (0x4be4) +#define AFE_SECURE_MASK_CONN47_5 (0x4be8) +#define AFE_SECURE_MASK_CONN48_5 (0x4bec) +#define AFE_SECURE_MASK_CONN49_5 (0x4bf0) +#define AFE_SECURE_MASK_CONN50_5 (0x4bf4) +#define AFE_SECURE_MASK_CONN51_5 (0x4bf8) +#define AFE_SECURE_MASK_CONN52_5 (0x4bfc) +#define AFE_GASRC0_NEW_CON0 (0x4c40) +#define AFE_GASRC0_NEW_CON1 (0x4c44) +#define AFE_GASRC0_NEW_CON2 (0x4c48) +#define AFE_GASRC0_NEW_CON3 (0x4c4c) +#define AFE_GASRC0_NEW_CON4 (0x4c50) +#define AFE_GASRC0_NEW_CON5 (0x4c54) +#define AFE_GASRC0_NEW_CON6 (0x4c58) +#define AFE_GASRC0_NEW_CON7 (0x4c5c) +#define AFE_GASRC0_NEW_CON8 (0x4c60) +#define AFE_GASRC0_NEW_CON9 (0x4c64) +#define AFE_GASRC0_NEW_CON10 (0x4c68) +#define AFE_GASRC0_NEW_CON11 (0x4c6c) +#define AFE_GASRC0_NEW_CON12 (0x4c70) +#define AFE_GASRC0_NEW_CON13 (0x4c74) +#define AFE_GASRC0_NEW_CON14 (0x4c78) +#define AFE_GASRC1_NEW_CON0 (0x4c80) +#define AFE_GASRC1_NEW_CON1 (0x4c84) +#define AFE_GASRC1_NEW_CON2 (0x4c88) +#define AFE_GASRC1_NEW_CON3 (0x4c8c) +#define AFE_GASRC1_NEW_CON4 (0x4c90) +#define AFE_GASRC1_NEW_CON5 (0x4c94) +#define AFE_GASRC1_NEW_CON6 (0x4c98) +#define AFE_GASRC1_NEW_CON7 (0x4c9c) +#define AFE_GASRC1_NEW_CON8 (0x4ca0) +#define AFE_GASRC1_NEW_CON9 (0x4ca4) +#define AFE_GASRC1_NEW_CON10 (0x4ca8) +#define AFE_GASRC1_NEW_CON11 (0x4cac) +#define AFE_GASRC1_NEW_CON12 (0x4cb0) +#define AFE_GASRC1_NEW_CON13 (0x4cb4) +#define AFE_GASRC1_NEW_CON14 (0x4cb8) +#define AFE_GASRC2_NEW_CON0 (0x4cc0) +#define AFE_GASRC2_NEW_CON1 (0x4cc4) +#define AFE_GASRC2_NEW_CON2 (0x4cc8) +#define AFE_GASRC2_NEW_CON3 (0x4ccc) +#define AFE_GASRC2_NEW_CON4 (0x4cd0) +#define AFE_GASRC2_NEW_CON5 (0x4cd4) +#define AFE_GASRC2_NEW_CON6 (0x4cd8) +#define AFE_GASRC2_NEW_CON7 (0x4cdc) +#define AFE_GASRC2_NEW_CON8 (0x4ce0) +#define AFE_GASRC2_NEW_CON9 (0x4ce4) +#define AFE_GASRC2_NEW_CON10 (0x4ce8) +#define AFE_GASRC2_NEW_CON11 (0x4cec) +#define AFE_GASRC2_NEW_CON12 (0x4cf0) +#define AFE_GASRC2_NEW_CON13 (0x4cf4) +#define AFE_GASRC2_NEW_CON14 (0x4cf8) +#define AFE_GASRC3_NEW_CON0 (0x4d00) +#define AFE_GASRC3_NEW_CON1 (0x4d04) +#define AFE_GASRC3_NEW_CON2 (0x4d08) +#define AFE_GASRC3_NEW_CON3 (0x4d0c) +#define AFE_GASRC3_NEW_CON4 (0x4d10) +#define AFE_GASRC3_NEW_CON5 (0x4d14) +#define AFE_GASRC3_NEW_CON6 (0x4d18) +#define AFE_GASRC3_NEW_CON7 (0x4d1c) +#define AFE_GASRC3_NEW_CON8 (0x4d20) +#define AFE_GASRC3_NEW_CON9 (0x4d24) +#define AFE_GASRC3_NEW_CON10 (0x4d28) +#define AFE_GASRC3_NEW_CON11 (0x4d2c) +#define AFE_GASRC3_NEW_CON12 (0x4d30) +#define AFE_GASRC3_NEW_CON13 (0x4d34) +#define AFE_GASRC3_NEW_CON14 (0x4d38) +#define AFE_GASRC4_NEW_CON0 (0x4d40) +#define AFE_GASRC4_NEW_CON1 (0x4d44) +#define AFE_GASRC4_NEW_CON2 (0x4d48) +#define AFE_GASRC4_NEW_CON3 (0x4d4c) +#define AFE_GASRC4_NEW_CON4 (0x4d50) +#define AFE_GASRC4_NEW_CON5 (0x4d54) +#define AFE_GASRC4_NEW_CON6 (0x4d58) +#define AFE_GASRC4_NEW_CON7 (0x4d5c) +#define AFE_GASRC4_NEW_CON8 (0x4d60) +#define AFE_GASRC4_NEW_CON9 (0x4d64) +#define AFE_GASRC4_NEW_CON10 (0x4d68) +#define AFE_GASRC4_NEW_CON11 (0x4d6c) +#define AFE_GASRC4_NEW_CON12 (0x4d70) +#define AFE_GASRC4_NEW_CON13 (0x4d74) +#define AFE_GASRC4_NEW_CON14 (0x4d78) +#define AFE_GASRC5_NEW_CON0 (0x4d80) +#define AFE_GASRC5_NEW_CON1 (0x4d84) +#define AFE_GASRC5_NEW_CON2 (0x4d88) +#define AFE_GASRC5_NEW_CON3 (0x4d8c) +#define AFE_GASRC5_NEW_CON4 (0x4d90) +#define AFE_GASRC5_NEW_CON5 (0x4d94) +#define AFE_GASRC5_NEW_CON6 (0x4d98) +#define AFE_GASRC5_NEW_CON7 (0x4d9c) +#define AFE_GASRC5_NEW_CON8 (0x4da0) +#define AFE_GASRC5_NEW_CON9 (0x4da4) +#define AFE_GASRC5_NEW_CON10 (0x4da8) +#define AFE_GASRC5_NEW_CON11 (0x4dac) +#define AFE_GASRC5_NEW_CON12 (0x4db0) +#define AFE_GASRC5_NEW_CON13 (0x4db4) +#define AFE_GASRC5_NEW_CON14 (0x4db8) +#define AFE_GASRC6_NEW_CON0 (0x4dc0) +#define AFE_GASRC6_NEW_CON1 (0x4dc4) +#define AFE_GASRC6_NEW_CON2 (0x4dc8) +#define AFE_GASRC6_NEW_CON3 (0x4dcc) +#define AFE_GASRC6_NEW_CON4 (0x4dd0) +#define AFE_GASRC6_NEW_CON5 (0x4dd4) +#define AFE_GASRC6_NEW_CON6 (0x4dd8) +#define AFE_GASRC6_NEW_CON7 (0x4ddc) +#define AFE_GASRC6_NEW_CON8 (0x4de0) +#define AFE_GASRC6_NEW_CON9 (0x4de4) +#define AFE_GASRC6_NEW_CON10 (0x4de8) +#define AFE_GASRC6_NEW_CON11 (0x4dec) +#define AFE_GASRC6_NEW_CON12 (0x4df0) +#define AFE_GASRC6_NEW_CON13 (0x4df4) +#define AFE_GASRC6_NEW_CON14 (0x4df8) +#define AFE_GASRC7_NEW_CON0 (0x4e00) +#define AFE_GASRC7_NEW_CON1 (0x4e04) +#define AFE_GASRC7_NEW_CON2 (0x4e08) +#define AFE_GASRC7_NEW_CON3 (0x4e0c) +#define AFE_GASRC7_NEW_CON4 (0x4e10) +#define AFE_GASRC7_NEW_CON5 (0x4e14) +#define AFE_GASRC7_NEW_CON6 (0x4e18) +#define AFE_GASRC7_NEW_CON7 (0x4e1c) +#define AFE_GASRC7_NEW_CON8 (0x4e20) +#define AFE_GASRC7_NEW_CON9 (0x4e24) +#define AFE_GASRC7_NEW_CON10 (0x4e28) +#define AFE_GASRC7_NEW_CON11 (0x4e2c) +#define AFE_GASRC7_NEW_CON12 (0x4e30) +#define AFE_GASRC7_NEW_CON13 (0x4e34) +#define AFE_GASRC7_NEW_CON14 (0x4e38) +#define AFE_GASRC8_NEW_CON0 (0x4e40) +#define AFE_GASRC8_NEW_CON1 (0x4e44) +#define AFE_GASRC8_NEW_CON2 (0x4e48) +#define AFE_GASRC8_NEW_CON3 (0x4e4c) +#define AFE_GASRC8_NEW_CON4 (0x4e50) +#define AFE_GASRC8_NEW_CON5 (0x4e54) +#define AFE_GASRC8_NEW_CON6 (0x4e58) +#define AFE_GASRC8_NEW_CON7 (0x4e5c) +#define AFE_GASRC8_NEW_CON8 (0x4e60) +#define AFE_GASRC8_NEW_CON9 (0x4e64) +#define AFE_GASRC8_NEW_CON10 (0x4e68) +#define AFE_GASRC8_NEW_CON11 (0x4e6c) +#define AFE_GASRC8_NEW_CON12 (0x4e70) +#define AFE_GASRC8_NEW_CON13 (0x4e74) +#define AFE_GASRC8_NEW_CON14 (0x4e78) +#define AFE_GASRC9_NEW_CON0 (0x4e80) +#define AFE_GASRC9_NEW_CON1 (0x4e84) +#define AFE_GASRC9_NEW_CON2 (0x4e88) +#define AFE_GASRC9_NEW_CON3 (0x4e8c) +#define AFE_GASRC9_NEW_CON4 (0x4e90) +#define AFE_GASRC9_NEW_CON5 (0x4e94) +#define AFE_GASRC9_NEW_CON6 (0x4e98) +#define AFE_GASRC9_NEW_CON7 (0x4e9c) +#define AFE_GASRC9_NEW_CON8 (0x4ea0) +#define AFE_GASRC9_NEW_CON9 (0x4ea4) +#define AFE_GASRC9_NEW_CON10 (0x4ea8) +#define AFE_GASRC9_NEW_CON11 (0x4eac) +#define AFE_GASRC9_NEW_CON12 (0x4eb0) +#define AFE_GASRC9_NEW_CON13 (0x4eb4) +#define AFE_GASRC9_NEW_CON14 (0x4eb8) +#define AFE_GASRC10_NEW_CON0 (0x4ec0) +#define AFE_GASRC10_NEW_CON1 (0x4ec4) +#define AFE_GASRC10_NEW_CON2 (0x4ec8) +#define AFE_GASRC10_NEW_CON3 (0x4ecc) +#define AFE_GASRC10_NEW_CON4 (0x4ed0) +#define AFE_GASRC10_NEW_CON5 (0x4ed4) +#define AFE_GASRC10_NEW_CON6 (0x4ed8) +#define AFE_GASRC10_NEW_CON7 (0x4edc) +#define AFE_GASRC10_NEW_CON8 (0x4ee0) +#define AFE_GASRC10_NEW_CON9 (0x4ee4) +#define AFE_GASRC10_NEW_CON10 (0x4ee8) +#define AFE_GASRC10_NEW_CON11 (0x4eec) +#define AFE_GASRC10_NEW_CON12 (0x4ef0) +#define AFE_GASRC10_NEW_CON13 (0x4ef4) +#define AFE_GASRC10_NEW_CON14 (0x4ef8) +#define AFE_GASRC11_NEW_CON0 (0x4f00) +#define AFE_GASRC11_NEW_CON1 (0x4f04) +#define AFE_GASRC11_NEW_CON2 (0x4f08) +#define AFE_GASRC11_NEW_CON3 (0x4f0c) +#define AFE_GASRC11_NEW_CON4 (0x4f10) +#define AFE_GASRC11_NEW_CON5 (0x4f14) +#define AFE_GASRC11_NEW_CON6 (0x4f18) +#define AFE_GASRC11_NEW_CON7 (0x4f1c) +#define AFE_GASRC11_NEW_CON8 (0x4f20) +#define AFE_GASRC11_NEW_CON9 (0x4f24) +#define AFE_GASRC11_NEW_CON10 (0x4f28) +#define AFE_GASRC11_NEW_CON11 (0x4f2c) +#define AFE_GASRC11_NEW_CON12 (0x4f30) +#define AFE_GASRC11_NEW_CON13 (0x4f34) +#define AFE_GASRC11_NEW_CON14 (0x4f38) + +#define AFE_IEC_BURST_INFO_MON (0x64b0) +#define AFE_SPDIFOUT_IP_VERSION (0x64b4) +#define AFE_SPDIF_OUT_CFG0 (0x64b8) +#define AFE_SPDIF_OUT_CFG1 (0x64bc) +#define AFE_SPDIF_OUT_CHSTS1 (0x64c0) +#define AFE_SPDIF_OUT_CHSTS2 (0x64c4) +#define AFE_SPDIF_OUT_CHSTS3 (0x64c8) +#define AFE_SPDIF_OUT_CHSTS4 (0x64cc) +#define AFE_SPDIF_OUT_CHSTS5 (0x64d0) +#define AFE_SPDIF_OUT_CHSTS6 (0x64d4) +#define AFE_SPDIF_OUT_USERCODE1 (0x64d8) +#define AFE_SPDIF_OUT_USERCODE2 (0x64dc) +#define AFE_SPDIF_OUT_USERCODE3 (0x64e0) +#define AFE_SPDIF_OUT_USERCODE4 (0x64e4) +#define AFE_SPDIF_OUT_USERCODE5 (0x64e8) +#define AFE_SPDIF_OUT_USERCODE6 (0x64ec) +#define AFE_SPDIF_OUT_BURST_PRE0 (0x64f0) +#define AFE_SPDIF_OUT_BURST_PRE1 (0x64f4) +#define AFE_SPDIF_OUT_MON0 (0x64f8) +#define AFE_SPDIF_OUT_MON1 (0x64fc) +#define AFE_SPDIF_OUT_MON2 (0x6500) +#define AFE_SPDIF_OUT_MON3 (0x6504) +#define AFE_SPDIF_OUT_MON4 (0x6508) +#define AFE_SPDIF_OUT_MON5 (0x650c) +#define AFE_CONN0_6 (0x7000) +#define AFE_CONN1_6 (0x7004) +#define AFE_CONN2_6 (0x7008) +#define AFE_CONN3_6 (0x700c) +#define AFE_CONN4_6 (0x7010) +#define AFE_CONN5_6 (0x7014) +#define AFE_CONN6_6 (0x7018) +#define AFE_CONN7_6 (0x701c) +#define AFE_CONN8_6 (0x7020) +#define AFE_CONN9_6 (0x7024) +#define AFE_CONN10_6 (0x7028) +#define AFE_CONN11_6 (0x702c) +#define AFE_CONN12_6 (0x7030) +#define AFE_CONN13_6 (0x7034) +#define AFE_CONN14_6 (0x7038) +#define AFE_CONN15_6 (0x703c) +#define AFE_CONN16_6 (0x7040) +#define AFE_CONN17_6 (0x7044) +#define AFE_CONN18_6 (0x7048) +#define AFE_CONN19_6 (0x704c) +#define AFE_CONN20_6 (0x7050) +#define AFE_CONN21_6 (0x7054) +#define AFE_CONN22_6 (0x7058) +#define AFE_CONN23_6 (0x705c) +#define AFE_CONN24_6 (0x7060) +#define AFE_CONN25_6 (0x7064) +#define AFE_CONN26_6 (0x7068) +#define AFE_CONN27_6 (0x706c) +#define AFE_CONN28_6 (0x7070) +#define AFE_CONN29_6 (0x7074) +#define AFE_CONN30_6 (0x7078) +#define AFE_CONN31_6 (0x707c) +#define AFE_CONN32_6 (0x7080) +#define AFE_CONN33_6 (0x7084) +#define AFE_CONN34_6 (0x7088) +#define AFE_CONN35_6 (0x708c) +#define AFE_CONN36_6 (0x7090) +#define AFE_CONN37_6 (0x7094) +#define AFE_CONN38_6 (0x7098) +#define AFE_CONN39_6 (0x709c) +#define AFE_CONN40_6 (0x70a0) +#define AFE_CONN41_6 (0x70a4) +#define AFE_CONN42_6 (0x70a8) +#define AFE_CONN43_6 (0x70ac) +#define AFE_CONN44_6 (0x70b0) +#define AFE_CONN45_6 (0x70b4) +#define AFE_CONN46_6 (0x70b8) +#define AFE_CONN47_6 (0x70bc) +#define AFE_CONN48_6 (0x70c0) +#define AFE_CONN49_6 (0x70c4) +#define AFE_CONN50_6 (0x70c8) +#define AFE_CONN51_6 (0x70cc) +#define AFE_CONN52_6 (0x70d0) +#define AFE_CONN53_6 (0x70d4) +#define AFE_CONN54_6 (0x70d8) +#define AFE_CONN55_6 (0x70dc) +#define AFE_CONN56_6 (0x70e0) +#define AFE_CONN57_6 (0x70e4) +#define AFE_CONN58_6 (0x70e8) +#define AFE_CONN59_6 (0x70ec) +#define AFE_CONN60_6 (0x70f0) +#define AFE_CONN61_6 (0x70f4) +#define AFE_CONN62_6 (0x70f8) +#define AFE_CONN63_6 (0x70fc) +#define AFE_CONN64_6 (0x7100) +#define AFE_CONN65_6 (0x7104) +#define AFE_CONN66_6 (0x7108) +#define AFE_CONN67_6 (0x710c) +#define AFE_CONN68_6 (0x7110) +#define AFE_CONN69_6 (0x7114) +#define AFE_CONN70_6 (0x7118) +#define AFE_CONN71_6 (0x711c) +#define AFE_CONN72_6 (0x7120) +#define AFE_CONN73_6 (0x7124) +#define AFE_CONN74_6 (0x7128) +#define AFE_CONN75_6 (0x712c) +#define AFE_CONN76_6 (0x7130) +#define AFE_CONN77_6 (0x7134) +#define AFE_CONN78_6 (0x7138) +#define AFE_CONN79_6 (0x713c) +#define AFE_CONN80_6 (0x7140) +#define AFE_CONN81_6 (0x7144) +#define AFE_CONN82_6 (0x7148) +#define AFE_CONN83_6 (0x714c) +#define AFE_CONN84_6 (0x7150) +#define AFE_CONN85_6 (0x7154) +#define AFE_CONN86_6 (0x7158) +#define AFE_CONN87_6 (0x715c) +#define AFE_CONN88_6 (0x7160) +#define AFE_CONN89_6 (0x7164) +#define AFE_CONN90_6 (0x7168) +#define AFE_CONN91_6 (0x716c) +#define AFE_CONN92_6 (0x7170) +#define AFE_CONN93_6 (0x7174) +#define AFE_CONN94_6 (0x7178) +#define AFE_CONN95_6 (0x717c) +#define AFE_CONN96_6 (0x7180) +#define AFE_CONN97_6 (0x7184) +#define AFE_CONN98_6 (0x7188) +#define AFE_CONN99_6 (0x718c) +#define AFE_CONN100_6 (0x7190) +#define AFE_CONN101_6 (0x7194) +#define AFE_CONN102_6 (0x7198) +#define AFE_CONN103_6 (0x719c) +#define AFE_CONN104_6 (0x71a0) +#define AFE_CONN105_6 (0x71a4) +#define AFE_CONN106_6 (0x71a8) +#define AFE_CONN107_6 (0x71ac) +#define AFE_CONN108_6 (0x71b0) +#define AFE_CONN109_6 (0x71b4) +#define AFE_CONN110_6 (0x71b8) +#define AFE_CONN111_6 (0x71bc) +#define AFE_CONN112_6 (0x71c0) +#define AFE_CONN113_6 (0x71c4) +#define AFE_CONN114_6 (0x71c8) +#define AFE_CONN115_6 (0x71cc) +#define AFE_CONN116_6 (0x71d0) +#define AFE_CONN117_6 (0x71d4) +#define AFE_CONN118_6 (0x71d8) +#define AFE_CONN119_6 (0x71dc) +#define AFE_CONN120_6 (0x71e0) +#define AFE_CONN121_6 (0x71e4) +#define AFE_CONN122_6 (0x71e8) +#define AFE_CONN123_6 (0x71ec) +#define AFE_CONN124_6 (0x71f0) +#define AFE_CONN125_6 (0x71f4) +#define AFE_CONN126_6 (0x71f8) +#define AFE_CONN127_6 (0x71fc) +#define AFE_CONN128_6 (0x7200) +#define AFE_CONN129_6 (0x7204) +#define AFE_CONN130_6 (0x7208) +#define AFE_CONN131_6 (0x720c) +#define AFE_CONN132_6 (0x7210) +#define AFE_CONN133_6 (0x7214) +#define AFE_CONN134_6 (0x7218) +#define AFE_CONN135_6 (0x721c) +#define AFE_CONN136_6 (0x7220) +#define AFE_CONN137_6 (0x7224) +#define AFE_CONN138_6 (0x7228) +#define AFE_CONN139_6 (0x722c) +#define AFE_CONN176_6 (0x72c0) +#define AFE_CONN177_6 (0x72c4) +#define AFE_CONN182_6 (0x72d8) +#define AFE_CONN183_6 (0x72dc) + +#define AFE_MAX_REGISTER (AFE_CONN183_6) + +/* PWR1_ASM_CON1 */ +#define PWR1_ASM_CON1_GASRC0_CALI_CK_SEL_MASK BIT(2) +#define PWR1_ASM_CON1_GASRC1_CALI_CK_SEL_MASK BIT(5) +#define PWR1_ASM_CON1_GASRC2_CALI_CK_SEL_MASK BIT(20) +#define PWR1_ASM_CON1_GASRC3_CALI_CK_SEL_MASK BIT(23) + +/* PWR1_ASM_CON2 */ +#define PWR1_ASM_CON2_GASRC4_CALI_CK_SEL_MASK BIT(2) +#define PWR1_ASM_CON2_GASRC5_CALI_CK_SEL_MASK BIT(7) +#define PWR1_ASM_CON2_GASRC6_CALI_CK_SEL_MASK BIT(12) +#define PWR1_ASM_CON2_GASRC7_CALI_CK_SEL_MASK BIT(17) +#define PWR1_ASM_CON2_GASRC8_CALI_CK_SEL_MASK BIT(22) +#define PWR1_ASM_CON2_GASRC9_CALI_CK_SEL_MASK BIT(27) + +/* PWR1_ASM_CON3 */ +#define PWR1_ASM_CON3_GASRC10_CALI_CK_SEL_MASK BIT(2) +#define PWR1_ASM_CON3_GASRC11_CALI_CK_SEL_MASK BIT(7) + +/* AUDIO_TOP_CON0 */ +#define AUDIO_TOP_CON0_PDN_AFE BIT(2) +#define AUDIO_TOP_CON0_PDN_APLL BIT(23) +#define AUDIO_TOP_CON0_PDN_APLL_TUNER BIT(19) +#define AUDIO_TOP_CON0_PDN_APLL2 BIT(24) +#define AUDIO_TOP_CON0_PDN_APLL2_TUNER BIT(20) +#define AUDIO_TOP_CON0_PDN_DAC BIT(25) +#define AUDIO_TOP_CON0_PDN_DAC_HIRES BIT(31) +#define AUDIO_TOP_CON0_PDN_DAC_PREDIS BIT(26) +#define AUDIO_TOP_CON0_PDN_SPDIFIN_TUNER BIT(10) +#define AUDIO_TOP_CON0_PDN_ADC BIT(28) +#define AUDIO_TOP_CON0_PDN_SPDF BIT(21) +#define AUDIO_TOP_CON0_PDN_TML BIT(27) +#define AUDIO_TOP_CON0_PDN_UL_TML BIT(18) + +/* AUDIO_TOP_CON1 */ +#define AUDIO_TOP_CON1_PDN_ADC_HIRES BIT(17) +#define AUDIO_TOP_CON1_PDN_ADDA6_ADC BIT(18) +#define AUDIO_TOP_CON1_PDN_ADDA6_HIRES BIT(19) +#define AUDIO_TOP_CON1_PDN_UL_TML_HIRES BIT(16) +#define AUDIO_TOP_CON1_PDN_DMIC_TML BIT(14) +#define AUDIO_TOP_CON1_PDN_A1SYS_HOPING BIT(2) +#define AUDIO_TOP_CON1_PDN_DMIC0 BIT(10) +#define AUDIO_TOP_CON1_PDN_DMIC1 BIT(11) +#define AUDIO_TOP_CON1_PDN_DMIC2 BIT(12) +#define AUDIO_TOP_CON1_PDN_DMIC3 BIT(13) + +/* AUDIO_TOP_CON3 */ +#define AUDIO_TOP_CON3_PDN_EARC_TUNER BIT(7) +#define AUDIO_TOP_CON3_PDN_LINEIN_TUNER BIT(5) + +/* AUDIO_TOP_CON4 */ +#define AUDIO_TOP_CON4_PDN_I2S_IN BIT(0) +#define AUDIO_TOP_CON4_PDN_TDM_IN BIT(1) +#define AUDIO_TOP_CON4_PDN_I2S_OUT BIT(6) +#define AUDIO_TOP_CON4_PDN_TDM_OUT BIT(7) +#define AUDIO_TOP_CON4_PDN_HDMI_OUT BIT(8) +#define AUDIO_TOP_CON4_PDN_ASRC11 BIT(16) +#define AUDIO_TOP_CON4_PDN_ASRC12 BIT(17) +#define AUDIO_TOP_CON4_PDN_A1SYS BIT(21) +#define AUDIO_TOP_CON4_PDN_A2SYS BIT(22) +#define AUDIO_TOP_CON4_PDN_A3SYS BIT(30) +#define AUDIO_TOP_CON4_PDN_A4SYS BIT(31) +#define AUDIO_TOP_CON4_PDN_PCMIF BIT(24) +#define AUDIO_TOP_CON4_PDN_INTDIR BIT(20) +#define AUDIO_TOP_CON4_PDN_MULTI_IN BIT(19) + +/* AUDIO_TOP_CON6 */ +#define AUDIO_TOP_CON6_PDN_GASRC11 BIT(11) +#define AUDIO_TOP_CON6_PDN_GASRC10 BIT(10) +#define AUDIO_TOP_CON6_PDN_GASRC9 BIT(9) +#define AUDIO_TOP_CON6_PDN_GASRC8 BIT(8) +#define AUDIO_TOP_CON6_PDN_GASRC7 BIT(7) +#define AUDIO_TOP_CON6_PDN_GASRC6 BIT(6) +#define AUDIO_TOP_CON6_PDN_GASRC5 BIT(5) +#define AUDIO_TOP_CON6_PDN_GASRC4 BIT(4) +#define AUDIO_TOP_CON6_PDN_GASRC3 BIT(3) +#define AUDIO_TOP_CON6_PDN_GASRC2 BIT(2) +#define AUDIO_TOP_CON6_PDN_GASRC1 BIT(1) +#define AUDIO_TOP_CON6_PDN_GASRC0 BIT(0) + +/* AFE_GAINx_CON0 */ +#define AFE_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8) +#define AFE_GAIN_CON0_GAIN_MODE_MASK GENMASK(7, 3) +#define AFE_GAIN_CON0_GAIN_ON_MASK BIT(0) + +/* AFE_GAINx_CON1 */ +#define AFE_GAIN_CON1_TARGET_MASK GENMASK(19, 0) + +/* AFE_GAINx_CON2 */ +#define AFE_GAIN_CON2_DOWN_STEP_MASK GENMASK(19, 0) + +/* AFE_GAINx_CON3 */ +#define AFE_GAIN_CON3_UP_STEP_MASK GENMASK(19, 0) + +/* AFE_GAINx_CUR */ +#define AFE_GAIN_CUR_GAIN_MASK GENMASK(19, 0) + +/* ASYS_TOP_CON */ +#define ASYS_TOP_CON_A1SYS_TIMING_ON BIT(0) +#define ASYS_TOP_CON_A2SYS_TIMING_ON BIT(1) +#define ASYS_TOP_CON_A3SYS_TIMING_ON BIT(4) +#define ASYS_TOP_CON_A4SYS_TIMING_ON BIT(5) +#define ASYS_TOP_CON_26M_TIMING_ON BIT(2) + +/* PWR2_TOP_CON0 */ +#define PWR2_TOP_CON_DMIC8_SRC_SEL_MASK GENMASK(31, 29) +#define PWR2_TOP_CON_DMIC7_SRC_SEL_MASK GENMASK(28, 26) +#define PWR2_TOP_CON_DMIC6_SRC_SEL_MASK GENMASK(25, 23) +#define PWR2_TOP_CON_DMIC5_SRC_SEL_MASK GENMASK(22, 20) +#define PWR2_TOP_CON_DMIC4_SRC_SEL_MASK GENMASK(19, 17) +#define PWR2_TOP_CON_DMIC3_SRC_SEL_MASK GENMASK(16, 14) +#define PWR2_TOP_CON_DMIC2_SRC_SEL_MASK GENMASK(13, 11) +#define PWR2_TOP_CON_DMIC1_SRC_SEL_MASK GENMASK(10, 8) + +/* PWR2_TOP_CON1 */ +#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1) + +/* PCM_INTF_CON1 */ +#define PCM_INTF_CON1_SYNC_OUT_INV BIT(23) +#define PCM_INTF_CON1_BCLK_OUT_INV BIT(22) +#define PCM_INTF_CON1_CLK_OUT_INV_MASK GENMASK(23, 22) +#define PCM_INTF_CON1_SYNC_IN_INV BIT(21) +#define PCM_INTF_CON1_BCLK_IN_INV BIT(20) +#define PCM_INTF_CON1_CLK_IN_INV_MASK GENMASK(21, 20) +#define PCM_INTF_CON1_PCM_24BIT BIT(16) +#define PCM_INTF_CON1_PCM_16BIT (0 << 16) +#define PCM_INTF_CON1_PCM_BIT_MASK BIT(16) +#define PCM_INTF_CON1_PCM_WLEN_32BCK (0 << 14) +#define PCM_INTF_CON1_PCM_WLEN_64BCK BIT(14) +#define PCM_INTF_CON1_PCM_WLEN_MASK BIT(14) +#define PCM_INTF_CON1_SYNC_LENGTH_MASK GENMASK(13, 9) +#define PCM_INTF_CON1_PCM_SLAVE BIT(5) +#define PCM_INTF_CON1_PCM_MASTER (0 << 5) +#define PCM_INTF_CON1_PCM_M_S_MASK BIT(5) +#define PCM_INTF_CON1_PCM_MODE_MASK GENMASK(4, 3) +#define PCM_INTF_CON1_PCM_FMT_MASK GENMASK(2, 1) +#define PCM_INTF_CON1_PCM_EN BIT(0) + +/* PCM_INTF_CON2 */ +#define PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK GENMASK(24, 23) +#define PCM_INTF_CON2_SYNC_FREQ_MODE_MASK GENMASK(16, 12) +#define PCM_INTF_CON2_PCM_TX2RX_LPBK BIT(8) + +/* AFE_MPHONE_MULTIx_CON0 */ +#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP BIT(3) +#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP_MASK BIT(3) +#define AFE_MPHONE_MULTI_CON0_24BIT_DATA (0x1 << 1) +#define AFE_MPHONE_MULTI_CON0_16BIT_DATA (0x0 << 1) +#define AFE_MPHONE_MULIT_CON0_24BIT_DATA_MASK BIT(1) +#define AFE_MPHONE_MULTI_CON0_EN BIT(0) + +/* AFE_MPHONE_MULTIx_CON1 */ +#define AFE_MPHONE_MULTI_CON1_SYNC_ON BIT(24) +#define AFE_MPHONE_MULTI_CON1_24BIT_SWAP_BYPASS BIT(22) +#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE (0x1 << 19) +#define AFE_MPHONE_MULTI_CON1_COMPACT_MODE (0x0 << 19) +#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE_MASK BIT(19) +#define AFE_MPHONE_MULTI_CON1_HBR_MODE BIT(18) +#define AFE_MPHONE_MULTI_CON1_LRCK_32_CYCLE (0x2 << 16) +#define AFE_MPHONE_MULTI_CON1_LRCK_24_CYCLE (0x1 << 16) +#define AFE_MPHONE_MULTI_CON1_LRCK_16_CYCLE (0x0 << 16) +#define AFE_MPHONE_MULTI_CON1_LRCK_CYCLE_SEL_MASK GENMASK(17, 16) +#define AFE_MPHONE_MULTI_CON1_LRCK_INV BIT(15) +#define AFE_MPHONE_MULTI_CON1_DELAY_DATA BIT(14) +#define AFE_MPHONE_MULTI_CON1_LEFT_ALIGN BIT(13) +#define AFE_MPHONE_MULTI_CON1_BIT_NUM_MASK GENMASK(12, 8) +#define AFE_MPHONE_MULTI_CON1_BCK_INV BIT(6) +#define AFE_MPHONE_MULTI_CON1_CH_NUM_MASK GENMASK(1, 0) + +/* AFE_MPHONE_MULTIx_CON2 */ +#define AFE_MPHONE_MULTI_CON2_SEL_SPDIFIN BIT(19) +/* AFE_AUD_PAD_TOP */ +#define RG_RX_PROTOCOL2 BIT(3) +#define RG_RX_FIFO_ON BIT(0) + +/* AFE_ADDA_MTKAIF_CFG0 */ +#define MTKAIF_RXIF_CLKINV_ADC BIT(31) +#define MTKAIF_RXIF_PROTOCOL2 BIT(16) +#define MTKAIF_TXIF_PROTOCOL2 BIT(4) +#define MTKAIF_TXIF_8TO5 BIT(2) +#define MTKAIF_RXIF_8TO5 BIT(1) +#define MTKAIF_IF_LOOPBACK1 BIT(0) + +/* AFE_ADDA_MTKAIF_RX_CFG2 */ +#define MTKAIF_RXIF_DELAY_CYCLE_MASK GENMASK(15, 12) +#define MTKAIF_RXIF_DELAY_DATA BIT(8) + +/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */ +#define ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE BIT(23) + +/* AFE_DMICx_UL_SRC_CON0 */ +#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(x) (((x) & 0x7) << 27) +#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(x) (((x) & 0x7) << 24) +#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_MASK GENMASK(29, 24) +#define AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL BIT(23) +#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL BIT(22) +#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL BIT(21) + +#define AFE_DMIC_UL_VOICE_MODE_MASK GENMASK(19, 17) +#define AFE_DMIC_UL_CON0_VOCIE_MODE_8K AFE_DMIC_UL_VOICE_MODE(0) +#define AFE_DMIC_UL_CON0_VOCIE_MODE_16K AFE_DMIC_UL_VOICE_MODE(1) +#define AFE_DMIC_UL_CON0_VOCIE_MODE_32K AFE_DMIC_UL_VOICE_MODE(2) +#define AFE_DMIC_UL_CON0_VOCIE_MODE_48K AFE_DMIC_UL_VOICE_MODE(3) +#define AFE_DMIC_UL_CON0_VOCIE_MODE_96K AFE_DMIC_UL_VOICE_MODE(4) +#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK GENMASK(9, 7) +#define AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL BIT(10) +#define AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL BIT(1) +#define AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL BIT(0) + +/* DMIC_BYPASS_HW_GAIN */ +#define DMIC_BYPASS_HW_GAIN4_ONE_HEART BIT(10) +#define DMIC_BYPASS_HW_GAIN3_ONE_HEART BIT(9) +#define DMIC_BYPASS_HW_GAIN2_ONE_HEART BIT(8) +#define DMIC_BYPASS_HW_GAIN_DMIC4_BYPASS BIT(4) +#define DMIC_BYPASS_HW_GAIN_DMIC3_BYPASS BIT(3) +#define DMIC_BYPASS_HW_GAIN_DMIC2_BYPASS BIT(2) +#define DMIC_BYPASS_HW_GAIN_DMIC1_BYPASS BIT(1) + +/* DMIC_GAINx_CON0 */ +#define DMIC_GAIN_CON0_GAIN_ON BIT(0) +#define DMIC_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8) + +/* DMIC_GAINx_CON1 */ +#define DMIC_GAIN_CON1_TARGET_MASK GENMASK(27, 0) + +/* DMIC_GAINx_CON2 */ +#define DMIC_GAIN_CON2_DOWN_STEP GENMASK(19, 0) + +/* DMIC_GAINx_CON3 */ +#define DMIC_GAIN_CON3_UP_STEP GENMASK(19, 0) + +/* DMIC_GAINx_CUR */ +#define DMIC_GAIN_CUR_GAIN_MASK GENMASK(27, 0) + +/* ETDM_INx_AFIFO_CON */ +#define ETDM_IN_USE_AFIFO BIT(8) +#define ETDM_IN_AFIFO_CLOCK_MASK GENMASK(7, 5) +#define ETDM_IN_AFIFO_MODE_MASK GENMASK(4, 0) + +/* ETDM_COWORK_CON0 */ +#define ETDM_OUT1_SLAVE_SEL_MASK GENMASK(23, 20) +#define ETDM_OUT1_SLAVE_SEL_SHIFT 20 +#define ETDM_OUT1_SYNC_SEL_MASK GENMASK(19, 16) +#define ETDM_OUT1_SYNC_SEL_SHIFT 16 + +/* ETDM_COWORK_CON1 */ +#define ETDM_IN1_SDATA_SEL_MASK GENMASK(23, 20) +#define ETDM_IN1_SDATA_SEL_SHIFT 20 +#define ETDM_IN1_SDATA0_SEL_MASK GENMASK(19, 16) +#define ETDM_IN1_SDATA0_SEL_SHIFT 16 +#define ETDM_IN1_SYNC_SEL_MASK GENMASK(15, 12) +#define ETDM_IN1_SYNC_SEL_SHIFT 12 +#define ETDM_IN1_SLAVE_SEL_MASK GENMASK(11, 8) +#define ETDM_IN1_SLAVE_SEL_SHIFT 8 + +/* ETDM_COWORK_CON2 */ +#define ETDM_IN2_SYNC_SEL_MASK GENMASK(31, 28) +#define ETDM_IN2_SYNC_SEL_SHIFT 28 +#define ETDM_IN2_SLAVE_SEL_MASK GENMASK(27, 24) +#define ETDM_IN2_SLAVE_SEL_SHIFT 24 +#define ETDM_OUT3_SLAVE_SEL_MASK GENMASK(23, 20) +#define ETDM_OUT3_SLAVE_SEL_SHIFT 20 +#define ETDM_OUT3_SYNC_SEL_MASK GENMASK(19, 16) +#define ETDM_OUT3_SYNC_SEL_SHIFT 16 +#define ETDM_OUT2_SLAVE_SEL_MASK GENMASK(11, 8) +#define ETDM_OUT2_SLAVE_SEL_SHIFT 8 +#define ETDM_OUT2_SYNC_SEL_MASK GENMASK(7, 4) +#define ETDM_OUT2_SYNC_SEL_SHIFT 4 + +/* ETDM_COWORK_CON3 */ +#define ETDM_IN2_SDATA_SEL_MASK GENMASK(7, 4) +#define ETDM_IN2_SDATA_SEL_SHIFT 4 +#define ETDM_IN2_SDATA0_SEL_MASK GENMASK(3, 0) +#define ETDM_IN2_SDATA0_SEL_SHIFT 0 + +/* ETDM_x_CONx */ +#define ETDM_CON0_CH_NUM_MASK GENMASK(27, 23) +#define ETDM_CON0_WORD_LEN_MASK GENMASK(20, 16) +#define ETDM_CON0_BIT_LEN_MASK GENMASK(15, 11) +#define ETDM_CON0_FORMAT_MASK GENMASK(8, 6) +#define ETDM_CON0_SLAVE_MODE BIT(5) +#define ETDM_CON0_SYNC_MODE BIT(1) +#define ETDM_CON0_EN BIT(0) + +#define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28) + +#define ETDM_CON1_MCLK_OUTPUT BIT(16) + +#define ETDM_IN_CON1_LRCK_AUTO_MODE BIT(31) +#define ETDM_IN_CON1_LRCK_WIDTH_MASK GENMASK(29, 20) + +#define ETDM_OUT_CON1_LRCK_AUTO_MODE BIT(29) +#define ETDM_OUT_CON1_LRCK_WIDTH_MASK GENMASK(28, 19) + +#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31) +#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK GENMASK(19, 15) +#define ETDM_IN_CON2_CLOCK_MASK GENMASK(12, 10) +#define ETDM_IN_CON2_CLOCK_SHIFT 10 +#define ETDM_IN_CON2_UPDATE_GAP_MASK GENMASK(9, 5) + +#define ETDM_OUT_CON2_LRCK_DELAY_BCK_INV BIT(30) +#define ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN BIT(29) + +#define ETDM_IN_CON3_FS_MASK GENMASK(30, 26) +#define ETDM_IN_CON3_DISABLE_OUT(x) BIT(((x) & 0xffff)) +#define ETDM_IN_CON3_DISABLE_OUT_MASK GENMASK(15, 0) + +#define ETDM_IN_CON4_MASTER_LRCK_INV BIT(19) +#define ETDM_IN_CON4_MASTER_BCK_INV BIT(18) +#define ETDM_IN_CON4_SLAVE_LRCK_INV BIT(17) +#define ETDM_IN_CON4_SLAVE_BCK_INV BIT(16) + +#define ETDM_OUT_CON4_RELATCH_EN_MASK GENMASK(28, 24) +#define ETDM_OUT_CON4_CLOCK_MASK GENMASK(8, 6) +#define ETDM_OUT_CON4_CLOCK_SHIFT 6 +#define ETDM_OUT_CON4_FS_MASK GENMASK(4, 0) + +#define ETDM_IN_CON5_LR_SWAP(x) BIT(((x) & 0xffff) + 16) +#define ETDM_IN_CON5_LR_SWAP_MASK GENMASK(31, 16) +#define ETDM_IN_CON5_ENABLE_ODD(x) BIT(((x) & 0xffff)) +#define ETDM_IN_CON5_ENABLE_ODD_MASK GENMASK(15, 0) + +#define ETDM_OUT_CON5_MASTER_LRCK_INV BIT(10) +#define ETDM_OUT_CON5_MASTER_BCK_INV BIT(9) +#define ETDM_OUT_CON5_SLAVE_LRCK_INV BIT(8) +#define ETDM_OUT_CON5_SLAVE_BCK_INV BIT(7) + +/* GASRC_TIMING_CON0 */ +#define GASRC_TIMING_CON0_GASRC0_IN_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON0_GASRC1_IN_MODE_MASK GENMASK(9, 5) +#define GASRC_TIMING_CON0_GASRC2_IN_MODE_MASK GENMASK(14, 10) +#define GASRC_TIMING_CON0_GASRC3_IN_MODE_MASK GENMASK(19, 15) +#define GASRC_TIMING_CON0_GASRC4_IN_MODE_MASK GENMASK(24, 20) +#define GASRC_TIMING_CON0_GASRC5_IN_MODE_MASK GENMASK(29, 25) + +/* GASRC_TIMING_CON1 */ +#define GASRC_TIMING_CON1_GASRC6_IN_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON1_GASRC7_IN_MODE_MASK GENMASK(9, 5) +#define GASRC_TIMING_CON1_GASRC8_IN_MODE_MASK GENMASK(14, 10) +#define GASRC_TIMING_CON1_GASRC9_IN_MODE_MASK GENMASK(19, 15) +#define GASRC_TIMING_CON1_GASRC10_IN_MODE_MASK GENMASK(24, 20) +#define GASRC_TIMING_CON1_GASRC11_IN_MODE_MASK GENMASK(29, 25) + +/* GASRC_TIMING_CON2 */ +#define GASRC_TIMING_CON2_GASRC12_IN_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON2_GASRC13_IN_MODE_MASK GENMASK(9, 5) +#define GASRC_TIMING_CON2_GASRC14_IN_MODE_MASK GENMASK(14, 10) +#define GASRC_TIMING_CON2_GASRC15_IN_MODE_MASK GENMASK(19, 15) +#define GASRC_TIMING_CON2_GASRC16_IN_MODE_MASK GENMASK(24, 20) +#define GASRC_TIMING_CON2_GASRC17_IN_MODE_MASK GENMASK(29, 25) + +/* GASRC_TIMING_CON3 */ +#define GASRC_TIMING_CON3_GASRC18_IN_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON3_GASRC19_IN_MODE_MASK GENMASK(9, 5) + +/* GASRC_TIMING_CON4 */ +#define GASRC_TIMING_CON4_GASRC0_OUT_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON4_GASRC1_OUT_MODE_MASK GENMASK(9, 5) +#define GASRC_TIMING_CON4_GASRC2_OUT_MODE_MASK GENMASK(14, 10) +#define GASRC_TIMING_CON4_GASRC3_OUT_MODE_MASK GENMASK(19, 15) +#define GASRC_TIMING_CON4_GASRC4_OUT_MODE_MASK GENMASK(24, 20) +#define GASRC_TIMING_CON4_GASRC5_OUT_MODE_MASK GENMASK(29, 25) + +/* GASRC_TIMING_CON5 */ +#define GASRC_TIMING_CON5_GASRC6_OUT_MODE_MASK GENMASK(4, 0) +#define GASRC_TIMING_CON5_GASRC7_OUT_MODE_MASK GENMASK(9, 5) +#define GASRC_TIMING_CON5_GASRC8_OUT_MODE_MASK GENMASK(14, 10) +#define GASRC_TIMING_CON5_GASRC9_OUT_MODE_MASK GENMASK(19, 15) +#define GASRC_TIMING_CON5_GASRC10_OUT_MODE_MASK GENMASK(24, 20) +#define GASRC_TIMING_CON5_GASRC11_OUT_MODE_MASK GENMASK(29, 25) + +/* AFE_DPTX_CON */ +#define AFE_DPTX_CON_CH_EN_2CH GENMASK(9, 8) +#define AFE_DPTX_CON_CH_EN_4CH GENMASK(11, 8) +#define AFE_DPTX_CON_CH_EN_6CH GENMASK(13, 8) +#define AFE_DPTX_CON_CH_EN_8CH GENMASK(15, 8) +#define AFE_DPTX_CON_CH_EN_MASK GENMASK(15, 8) +#define AFE_DPTX_CON_16BIT (0x1 << 2) +#define AFE_DPTX_CON_24BIT (0x0 << 2) +#define AFE_DPTX_CON_16BIT_MASK BIT(2) +#define AFE_DPTX_CON_CH_NUM_2CH (0x0 << 1) +#define AFE_DPTX_CON_CH_NUM_8CH (0x1 << 1) +#define AFE_DPTX_CON_CH_NUM_MASK BIT(1) +#define AFE_DPTX_CON_ON BIT(0) + +/* AFE_ADDA_DL_SRC2_CON0 */ +#define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28) +#define DL_2_CH1_SATURATION_EN_CTL BIT(27) +#define DL_2_CH2_SATURATION_EN_CTL BIT(26) +#define DL_2_MUTE_CH1_OFF_CTL_PRE BIT(12) +#define DL_2_MUTE_CH2_OFF_CTL_PRE BIT(11) +#define DL_2_VOICE_MODE_CTL_PRE BIT(5) +#define DL_2_GAIN_ON_CTL_PRE_SHIFT 1 +#define DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT 0 + +/* AFE_ADDA_DL_SRC2_CON1 */ +#define DL_2_GAIN_CTL_PRE_MASK GENMASK(31, 16) +#define DL_2_GAIN_CTL_PRE_SHIFT 16 + +/* AFE_ADDA_TOP_CON0 */ +#define C_LOOPBACK_MODE_CTL_MASK GENMASK(15, 12) +#define DL_INPUT_FROM_SINEGEN (4 << 12) + +/* AFE_ADDA_UL_DL_CON0 */ +#define ADDA_AFE_ON_SHIFT 0 + +/* AFE_ADDA_DL_SDM_DCCOMP_CON */ +#define DL_USE_NEW_2ND_SDM BIT(30) +#define ATTGAIN_CTL_MASK GENMASK(5, 0) + +/* AFE_ADDA_UL_SRC_CON0 */ +#define UL_MODE_3P25M_CH2_CTL BIT(22) +#define UL_MODE_3P25M_CH1_CTL BIT(21) +#define UL_VOICE_MODE_CTL_MASK GENMASK(19, 17) +#define UL_LOOPBACK_MODE_CTL BIT(2) +#define UL_SDM3_LEVEL_CTL BIT(1) +#define UL_SRC_ON_TMP_CTL_SHIFT 0 + +/* AFE_GASRCx_NEW_CON0 */ +#define AFE_GASRC_NEW_CON0_ONE_HEART BIT(31) +#define AFE_GASRC_NEW_CON0_CHSET0_CLR_IIR_HISTORY BIT(17) +#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_MASK GENMASK(15, 14) +#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_TX (0 << 14) +#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_RX BIT(14) +#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_MASK GENMASK(13, 12) +#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_TX (3 << 12) +#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_RX (2 << 12) +#define AFE_GASRC_NEW_CON0_CHSET0_IIR_EN BIT(11) +#define AFE_GASRC_NEW_CON0_CHSET0_IIR_STAGE_MASK GENMASK(10, 8) +#define AFE_GASRC_NEW_CON0_CHSET_STR_CLR BIT(4) +#define AFE_GASRC_NEW_CON0_COEFF_SRAM_CTRL BIT(1) +#define AFE_GASRC_NEW_CON0_ASM_ON BIT(0) + +/* AFE_GASRCx_NEW_CON5 */ +#define AFE_GASRC_NEW_CON5_CALI_LRCK_SEL_MASK GENMASK(3, 1) +#define AFE_GASRC_NEW_CON5_SOFT_RESET BIT(0) + +/* AFE_GASRCx_NEW_CON6 */ +#define AFE_GASRC_NEW_CON6_FREQ_CALI_CYCLE_MASK GENMASK(31, 16) +#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ3 BIT(12) +#define AFE_GASRC_NEW_CON6_COMP_FREQ_RES_EN BIT(11) +#define AFE_GASRC_NEW_CON6_CALI_SIG_MUX_SEL_MASK GENMASK(9, 8) +#define AFE_GASRC_NEW_CON6_FREQ_CALI_BP_DGL BIT(7) +#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ2 BIT(3) +#define AFE_GASRC_NEW_CON6_FREQ_CALI_AUTO_RESTART BIT(2) +#define AFE_GASRC_NEW_CON6_CALI_USE_FREQ_OUT BIT(1) +#define AFE_GASRC_NEW_CON6_CALI_EN BIT(0) + +/* AFE_GASRCx_NEW_CON7 */ +#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_MASK GENMASK(23, 0) +#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_49M (0x3c00) +#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_45M (0x3720) + +#endif diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c index 239e3f5b53d3..2847a2e747be 100644 --- a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c @@ -273,6 +273,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt8192_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -283,10 +285,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, + c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c index caceb0deb467..051433689ff5 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -213,11 +213,14 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); + dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", __func__, dai->id, substream->stream, - dai->playback_widget->active, dai->capture_widget->active); + p->active, c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; return mtk_dai_pcm_configure(substream, dai); diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c index 57e6e7160d2f..59e00a74b5f8 100644 --- a/sound/soc/meson/aiu-fifo-i2s.c +++ b/sound/soc/meson/aiu-fifo-i2s.c @@ -88,7 +88,7 @@ static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); unsigned int val; int ret; @@ -158,7 +158,7 @@ int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai) if (ret) return ret; - fifo = dai->playback_dma_data; + fifo = snd_soc_dai_dma_data_get_playback(dai); fifo->pcm = &fifo_i2s_pcm; fifo->mem_offset = AIU_MEM_I2S_START; diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index 2fb30f89bf7a..ddbd2fc40185 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -173,7 +173,7 @@ int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) if (ret) return ret; - fifo = dai->playback_dma_data; + fifo = snd_soc_dai_dma_data_get_playback(dai); fifo->pcm = &fifo_spdif_pcm; fifo->mem_offset = AIU_MEM_IEC958_START; diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c index d67ff4cdabd5..543d41856c12 100644 --- a/sound/soc/meson/aiu-fifo.c +++ b/sound/soc/meson/aiu-fifo.c @@ -34,7 +34,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = aiu_fifo_dai(substream); - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int addr; @@ -46,7 +46,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN | AIU_MEM_CONTROL_EMPTY_EN); @@ -80,7 +80,7 @@ int aiu_fifo_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); snd_soc_component_update_bits(component, fifo->mem_offset + AIU_MEM_CONTROL, @@ -98,7 +98,7 @@ int aiu_fifo_hw_params(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); dma_addr_t end; /* Setup the fifo boundaries */ @@ -132,7 +132,7 @@ static irqreturn_t aiu_fifo_isr(int irq, void *dev_id) int aiu_fifo_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); int ret; snd_soc_set_runtime_hwparams(substream, fifo->pcm); @@ -168,7 +168,7 @@ int aiu_fifo_startup(struct snd_pcm_substream *substream, void aiu_fifo_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); free_irq(fifo->irq, substream); clk_disable_unprepare(fifo->pclk); @@ -178,7 +178,7 @@ int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_card *card = rtd->card->snd_card; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); size_t size = fifo->pcm->buffer_bytes_max; int ret; @@ -200,15 +200,16 @@ int aiu_fifo_dai_probe(struct snd_soc_dai *dai) if (!fifo) return -ENOMEM; - dai->playback_dma_data = fifo; + snd_soc_dai_dma_data_set_playback(dai, fifo); return 0; } int aiu_fifo_dai_remove(struct snd_soc_dai *dai) { - kfree(dai->playback_dma_data); + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); + + kfree(fifo); return 0; } - diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index c040c83637e0..7624aafe9009 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -37,10 +37,8 @@ int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, unsigned int slot_width) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); - struct axg_tdm_stream *tx = (struct axg_tdm_stream *) - dai->playback_dma_data; - struct axg_tdm_stream *rx = (struct axg_tdm_stream *) - dai->capture_dma_data; + struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai); + struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai); unsigned int tx_slots, rx_slots; unsigned int fmt = 0; @@ -362,11 +360,14 @@ static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) { - if (dai->capture_dma_data) - axg_tdm_stream_free(dai->capture_dma_data); + int stream; - if (dai->playback_dma_data) - axg_tdm_stream_free(dai->playback_dma_data); + for_each_pcm_streams(stream) { + struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream); + + if (ts) + axg_tdm_stream_free(ts); + } return 0; } @@ -374,19 +375,20 @@ static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int stream; - if (dai->capture_widget) { - dai->capture_dma_data = axg_tdm_stream_alloc(iface); - if (!dai->capture_dma_data) - return -ENOMEM; - } + for_each_pcm_streams(stream) { + struct axg_tdm_stream *ts; + + if (!snd_soc_dai_get_widget(dai, stream)) + continue; - if (dai->playback_widget) { - dai->playback_dma_data = axg_tdm_stream_alloc(iface); - if (!dai->playback_dma_data) { + ts = axg_tdm_stream_alloc(iface); + if (!ts) { axg_tdm_iface_remove_dai(dai); return -ENOMEM; } + snd_soc_dai_dma_data_set(dai, stream, ts); } return 0; diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c index 49b613a1faf2..c8f6ea24ae78 100644 --- a/sound/soc/meson/axg-tdmin.c +++ b/sound/soc/meson/axg-tdmin.c @@ -83,7 +83,7 @@ axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w) if (!be) return NULL; - return be->capture_dma_data; + return snd_soc_dai_dma_data_get_capture(be); } static void axg_tdmin_enable(struct regmap *map) diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c index 22d519fc07b2..c4039e4f0847 100644 --- a/sound/soc/meson/axg-tdmout.c +++ b/sound/soc/meson/axg-tdmout.c @@ -81,7 +81,7 @@ axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w) if (!be) return NULL; - return be->playback_dma_data; + return snd_soc_dai_dma_data_get_playback(be); } static void axg_tdmout_enable(struct regmap *map) diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c index 80c5ed196961..5913486c43ab 100644 --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -39,13 +39,13 @@ meson_codec_glue_get_input(struct snd_soc_dapm_widget *w) static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai, struct meson_codec_glue_input *data) { - dai->playback_dma_data = data; + snd_soc_dai_dma_data_set_playback(dai, data); } struct meson_codec_glue_input * meson_codec_glue_input_get_data(struct snd_soc_dai *dai) { - return dai->playback_dma_data; + return snd_soc_dai_dma_data_get_playback(dai); } EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data); @@ -99,8 +99,8 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct meson_codec_glue_input *in_data = - meson_codec_glue_output_get_input_data(dai->capture_widget); + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai); + struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w); if (!in_data) return -ENODEV; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index c26d1b36e8f7..e6bca9070953 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -12,7 +12,10 @@ config SND_PXA2XX_AC97 tristate config SND_PXA2XX_SOC_AC97 - tristate + tristate "SoC AC97 support for PXA2xx" + depends on SND_PXA2XX_SOC + depends on AC97_BUS=n + default y select AC97_BUS_NEW select SND_PXA2XX_LIB select SND_PXA2XX_LIB_AC97 diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 44303b6eb228..70442315f5c5 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -306,18 +306,12 @@ static int spitz_probe(struct platform_device *pdev) return ret; } -static int spitz_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver spitz_driver = { .driver = { .name = "spitz-audio", .pm = &snd_soc_pm_ops, }, .probe = spitz_probe, - .remove = spitz_remove, }; module_platform_driver(spitz_driver); diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index c7b7d0864d1a..5d07b38f6d72 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -113,6 +113,7 @@ static int apq8096_platform_probe(struct platform_device *pdev) if (!card) return -ENOMEM; + card->driver_name = "apq8096"; card->dev = dev; card->owner = THIS_MODULE; dev_set_drvdata(dev, card); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index b41ab7a321ae..ef5cb40b2d9b 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1181,7 +1181,7 @@ static int lpass_platform_pcm_new(struct snd_soc_component *component, if (is_cdc_dma_port(dai_id)) return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id); - return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, + return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_NONCOHERENT, component->dev, size); } diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index ee59ef36b85a..7f02f5b2c33f 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -8,6 +8,7 @@ #include <linux/slab.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <linux/spinlock.h> #include <sound/pcm.h> #include <asm/dma.h> #include <linux/dma-mapping.h> @@ -53,6 +54,7 @@ struct q6apm_dai_rtd { uint16_t session_id; enum stream_state state; struct q6apm_graph *graph; + spinlock_t lock; }; struct q6apm_dai_data { @@ -62,7 +64,8 @@ struct q6apm_dai_data { static struct snd_pcm_hardware q6apm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_BATCH), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE), .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, @@ -80,7 +83,8 @@ static struct snd_pcm_hardware q6apm_dai_hardware_capture = { static struct snd_pcm_hardware q6apm_dai_hardware_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_BATCH), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE), .rates = SNDRV_PCM_RATE_8000_192000, .rate_min = 8000, @@ -99,20 +103,25 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo { struct q6apm_dai_rtd *prtd = priv; struct snd_pcm_substream *substream = prtd->substream; + unsigned long flags; switch (opcode) { case APM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6APM_STREAM_STOPPED; break; case APM_CLIENT_EVENT_DATA_WRITE_DONE: + spin_lock_irqsave(&prtd->lock, flags); prtd->pos += prtd->pcm_count; + spin_unlock_irqrestore(&prtd->lock, flags); snd_pcm_period_elapsed(substream); if (prtd->state == Q6APM_STREAM_RUNNING) q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0); break; case APM_CLIENT_EVENT_DATA_READ_DONE: + spin_lock_irqsave(&prtd->lock, flags); prtd->pos += prtd->pcm_count; + spin_unlock_irqrestore(&prtd->lock, flags); snd_pcm_period_elapsed(substream); if (prtd->state == Q6APM_STREAM_RUNNING) q6apm_read(prtd->graph); @@ -253,6 +262,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, if (prtd == NULL) return -ENOMEM; + spin_lock_init(&prtd->lock); prtd->substream = substream; prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id); if (IS_ERR(prtd->graph)) { @@ -332,11 +342,17 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; + snd_pcm_uframes_t ptr; + unsigned long flags; + spin_lock_irqsave(&prtd->lock, flags); if (prtd->pos == prtd->pcm_size) prtd->pos = 0; - return bytes_to_frames(runtime, prtd->pos); + ptr = bytes_to_frames(runtime, prtd->pos); + spin_unlock_irqrestore(&prtd->lock, flags); + + return ptr; } static int q6apm_dai_hw_params(struct snd_soc_component *component, diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index ce9e5646d8f3..23d23bc6fbaa 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -127,6 +127,11 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s int graph_id = dai->id; int rc; + if (dai_data->is_port_started[dai->id]) { + q6apm_graph_stop(dai_data->graph[dai->id]); + dai_data->is_port_started[dai->id] = false; + } + /** * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 5beb898f28f5..994c9e823a88 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -27,6 +27,8 @@ struct apm_graph_mgmt_cmd { #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8) +struct q6apm *g_apm; + int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode) { gpr_device_t *gdev = apm->gdev; @@ -143,6 +145,7 @@ static void q6apm_put_audioreach_graph(struct kref *ref) kfree(graph); } + static int q6apm_get_apm_state(struct q6apm *apm) { struct gpr_pkt *pkt; @@ -158,6 +161,15 @@ static int q6apm_get_apm_state(struct q6apm *apm) return apm->state; } +bool q6apm_is_adsp_ready(void) +{ + if (g_apm) + return q6apm_get_apm_state(g_apm); + + return false; +} +EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready); + static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm, struct audioreach_graph_info *info, uint32_t mid) @@ -658,6 +670,8 @@ static int apm_probe(gpr_device_t *gdev) idr_init(&apm->modules_idr); + g_apm = apm; + q6apm_get_apm_state(apm); ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0); diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 273f97812741..7005be9b63e3 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -145,4 +145,6 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv); int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph); +bool q6apm_is_adsp_ready(void); + #endif /* __APM_GRAPH_ */ diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c index 8aa1a213bfb7..3aa63aac4a68 100644 --- a/sound/soc/qcom/qdsp6/q6prm.c +++ b/sound/soc/qcom/qdsp6/q6prm.c @@ -12,6 +12,7 @@ #include <linux/soc/qcom/apr.h> #include <dt-bindings/soc/qcom,gpr.h> #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h> +#include "q6apm.h" #include "q6prm.h" #include "audioreach.h" @@ -226,6 +227,9 @@ static int prm_probe(gpr_device_t *gdev) init_waitqueue_head(&cc->wait); dev_set_drvdata(dev, cc); + if (!q6apm_is_adsp_ready()) + return -EPROBE_DEFER; + return devm_of_platform_populate(dev); } diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index d8d35563af00..02612af714a8 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -588,6 +588,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) static const struct of_device_id sdm845_snd_device_id[] = { { .compatible = "qcom,sdm845-sndcard" }, + /* Do not grow the list for compatible devices */ { .compatible = "qcom,db845c-sndcard" }, { .compatible = "lenovo,yoga-c630-sndcard" }, {}, diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 42f76bc0fb02..f98a2fa85edd 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -13,7 +13,7 @@ config SND_SOC_ROCKCHIP_I2S select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for - Rockchip I2S device. The device supports upto maximum of + Rockchip I2S device. The device supports up to maximum of 8 channels each for play and record. config SND_SOC_ROCKCHIP_I2S_TDM diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 08b90ec5cc80..166257c6ae14 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1070,9 +1070,9 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); if (i2s_tdm->has_capture) - dai->capture_dma_data = &i2s_tdm->capture_dma_data; + snd_soc_dai_dma_data_set_capture(dai, &i2s_tdm->capture_dma_data); if (i2s_tdm->has_playback) - dai->playback_dma_data = &i2s_tdm->playback_dma_data; + snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data); if (i2s_tdm->mclk_calibrate) snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 5b1e47bdc376..6ce92b1db790 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -373,7 +373,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) { struct rk_pdm_dev *pdm = to_info(dai); - dai->capture_dma_data = &pdm->capture_dma_data; + snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data); return 0; } diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5b4f00457587..2d937fcf357d 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -196,7 +196,7 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); - dai->playback_dma_data = &spdif->playback_dma_data; + snd_soc_dai_dma_data_set_playback(dai, &spdif->playback_dma_data); return 0; } diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index b4b5d6053503..138e95581979 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -132,6 +132,7 @@ #define EXYNOS7_MOD_RCLK_192FS 7 #define PSR_PSREN (1 << 15) +#define PSR_PSVAL(x) ((((x) - 1) << 8) & 0x3f00) #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9505200f3d11..6f96032090de 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -50,6 +50,10 @@ struct samsung_i2s_dai_data { u32 quirks; unsigned int pcm_rates; const struct samsung_i2s_variant_regs *i2s_variant_regs; + void (*fixup_early)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + void (*fixup_late)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); }; struct i2s_dai { @@ -111,6 +115,10 @@ struct samsung_i2s_priv { u32 suspend_i2spsr; const struct samsung_i2s_variant_regs *variant_regs; + void (*fixup_early)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + void (*fixup_late)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); u32 quirks; /* The clock provider's data */ @@ -940,6 +948,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: pm_runtime_get_sync(dai->dev); + + if (priv->fixup_early) + priv->fixup_early(substream, dai); + spin_lock_irqsave(&priv->lock, flags); if (config_setup(i2s)) { @@ -947,6 +959,9 @@ static int i2s_trigger(struct snd_pcm_substream *substream, return -EINVAL; } + if (priv->fixup_late) + priv->fixup_late(substream, dai); + if (capture) i2s_rxctrl(i2s, 1); else @@ -1410,6 +1425,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (np) { priv->quirks = i2s_dai_data->quirks; + priv->fixup_early = i2s_dai_data->fixup_early; + priv->fixup_late = i2s_dai_data->fixup_late; } else { if (!i2s_pdata) { dev_err(&pdev->dev, "Missing platform data\n"); @@ -1563,6 +1580,31 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); + struct i2s_dai *other = get_other_dai(i2s); + + if (!is_opened(other)) { + i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_OUT); + i2s_set_sysclk(dai, SAMSUNG_I2S_OPCLK, 0, MOD_OPCLK_PCLK); + } +} + +static void fsd_i2s_fixup_late(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); + struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); + struct i2s_dai *other = get_other_dai(i2s); + + if (!is_opened(other)) + writel(PSR_PSVAL(2) | PSR_PSREN, priv->addr + I2SPSR); +} + static const struct samsung_i2s_variant_regs i2sv3_regs = { .bfs_off = 1, .rfs_off = 3, @@ -1652,6 +1694,14 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = { .i2s_variant_regs = &i2sv5_i2s1_regs, }; +static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = { + .quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, + .pcm_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_variant_regs = &i2sv7_regs, + .fixup_early = fsd_i2s_fixup_early, + .fixup_late = fsd_i2s_fixup_late, +}; + static const struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", @@ -1678,6 +1728,9 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,exynos7-i2s1", .data = &i2sv5_dai_type_i2s1, + }, { + .compatible = "tesla,fsd-i2s", + .data = &fsd_dai_type, }, {}, }; diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index ca1e1281cefa..7bc4421835d7 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -11,7 +11,7 @@ #define CLKB 1 #define CLKC 2 #define CLKI 3 -#define CLKMAX 4 +#define CLKINMAX 4 #define CLKOUT 0 #define CLKOUT1 1 @@ -25,44 +25,56 @@ static struct rsnd_mod_ops adg_ops = { .name = "adg", }; +#define ADG_HZ_441 0 +#define ADG_HZ_48 1 +#define ADG_HZ_SIZE 2 + struct rsnd_adg { - struct clk *clk[CLKMAX]; + struct clk *clkin[CLKINMAX]; struct clk *clkout[CLKOUTMAX]; struct clk *null_clk; struct clk_onecell_data onecell; struct rsnd_mod mod; - int clk_rate[CLKMAX]; - u32 flags; + int clkin_rate[CLKINMAX]; + int clkin_size; + int clkout_size; u32 ckr; u32 rbga; u32 rbgb; - int rbga_rate_for_441khz; /* RBGA */ - int rbgb_rate_for_48khz; /* RBGB */ + int rbg_rate[ADG_HZ_SIZE]; /* RBGA / RBGB */ }; -#define LRCLK_ASYNC (1 << 0) -#define AUDIO_OUT_48 (1 << 1) - -#define for_each_rsnd_clk(pos, adg, i) \ +#define for_each_rsnd_clkin(pos, adg, i) \ for (i = 0; \ - (i < CLKMAX) && \ - ((pos) = adg->clk[i]); \ + (i < adg->clkin_size) && \ + ((pos) = adg->clkin[i]); \ i++) #define for_each_rsnd_clkout(pos, adg, i) \ for (i = 0; \ - (i < CLKOUTMAX) && \ + (i < adg->clkout_size) && \ ((pos) = adg->clkout[i]); \ i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) -static const char * const clk_name[] = { +static const char * const clkin_name_gen4[] = { + [CLKA] = "clkin", +}; + +static const char * const clkin_name_gen2[] = { [CLKA] = "clk_a", [CLKB] = "clk_b", [CLKC] = "clk_c", [CLKI] = "clk_i", }; +static const char * const clkout_name_gen2[] = { + [CLKOUT] = "audio_clkout", + [CLKOUT1] = "audio_clkout1", + [CLKOUT2] = "audio_clkout2", + [CLKOUT3] = "audio_clkout3", +}; + static u32 rsnd_adg_calculate_rbgx(unsigned long div) { int i; @@ -116,11 +128,11 @@ static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, unsigned int val, en; unsigned int min, diff; unsigned int sel_rate[] = { - adg->clk_rate[CLKA], /* 0000: CLKA */ - adg->clk_rate[CLKB], /* 0001: CLKB */ - adg->clk_rate[CLKC], /* 0010: CLKC */ - adg->rbga_rate_for_441khz, /* 0011: RBGA */ - adg->rbgb_rate_for_48khz, /* 0100: RBGB */ + adg->clkin_rate[CLKA], /* 0000: CLKA */ + adg->clkin_rate[CLKB], /* 0001: CLKB */ + adg->clkin_rate[CLKC], /* 0010: CLKC */ + adg->rbg_rate[ADG_HZ_441], /* 0011: RBGA */ + adg->rbg_rate[ADG_HZ_48], /* 0100: RBGB */ }; min = ~0; @@ -291,6 +303,7 @@ 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,17 +316,17 @@ 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 (i = 0; i < CLKMAX; i++) - if (rate == adg->clk_rate[i]) + for_each_rsnd_clkin(clk, adg, i) + if (rate == adg->clkin_rate[i]) return sel_table[i]; /* * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz) + if (rate == adg->rbg_rate[ADG_HZ_441]) return 0x10; - if (rate == adg->rbgb_rate_for_48khz) + if (rate == adg->rbg_rate[ADG_HZ_48]) return 0x20; return -EIO; @@ -341,13 +354,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_adg_set_ssi_clk(ssi_mod, data); - if (rsnd_flags_has(adg, LRCLK_ASYNC)) { - if (rsnd_flags_has(adg, AUDIO_OUT_48)) - ckr = 0x80000000; - } else { - if (0 == (rate % 8000)) - ckr = 0x80000000; - } + if (0 == (rate % 8000)) + ckr = 0x80000000; /* BRGB output = 48kHz */ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); rsnd_mod_write(adg_mod, BRRA, adg->rbga); @@ -355,8 +363,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", (ckr) ? 'B' : 'A', - (ckr) ? adg->rbgb_rate_for_48khz : - adg->rbga_rate_for_441khz); + (ckr) ? adg->rbg_rate[ADG_HZ_48] : + adg->rbg_rate[ADG_HZ_441]); return 0; } @@ -367,7 +375,7 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) struct clk *clk; int i; - for_each_rsnd_clk(clk, adg, i) { + for_each_rsnd_clkin(clk, adg, i) { if (enable) { clk_prepare_enable(clk); @@ -376,7 +384,7 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) * atomic context. Let's keep it when * rsnd_adg_clk_enable() was called */ - adg->clk_rate[i] = clk_get_rate(clk); + adg->clkin_rate[i] = clk_get_rate(clk); } else { clk_disable_unprepare(clk); } @@ -425,19 +433,30 @@ 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; + const char * const *clkin_name; + int clkin_size; int i; - for (i = 0; i < CLKMAX; i++) { - clk = devm_clk_get(dev, clk_name[i]); + clkin_name = clkin_name_gen2; + clkin_size = ARRAY_SIZE(clkin_name_gen2); + if (rsnd_is_gen4(priv)) { + clkin_name = clkin_name_gen4; + clkin_size = ARRAY_SIZE(clkin_name_gen4); + } + + for (i = 0; i < clkin_size; i++) { + clk = devm_clk_get(dev, clkin_name[i]); if (IS_ERR_OR_NULL(clk)) clk = rsnd_adg_null_clk_get(priv); if (IS_ERR_OR_NULL(clk)) goto err; - adg->clk[i] = clk; + adg->clkin[i] = clk; } + adg->clkin_size = clkin_size; + return 0; err: @@ -467,18 +486,13 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) struct property *prop; u32 ckr, rbgx, rbga, rbgb; u32 rate, div; -#define REQ_SIZE 2 - u32 req_rate[REQ_SIZE] = {}; + u32 req_rate[ADG_HZ_SIZE] = {}; uint32_t count = 0; - unsigned long req_48kHz_rate, req_441kHz_rate; + unsigned long req_Hz[ADG_HZ_SIZE]; + int clkout_size; int i, req_size; const char *parent_clk_name = NULL; - static const char * const clkout_name[] = { - [CLKOUT] = "audio_clkout", - [CLKOUT1] = "audio_clkout1", - [CLKOUT2] = "audio_clkout2", - [CLKOUT3] = "audio_clkout3", - }; + const char * const *clkout_name; int brg_table[] = { [CLKA] = 0x0, [CLKB] = 0x1, @@ -499,27 +513,21 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) goto rsnd_adg_get_clkout_end; req_size = prop->length / sizeof(u32); - if (req_size > REQ_SIZE) { + if (req_size > ADG_HZ_SIZE) { dev_err(dev, "too many clock-frequency\n"); return -EINVAL; } of_property_read_u32_array(np, "clock-frequency", req_rate, req_size); - req_48kHz_rate = 0; - req_441kHz_rate = 0; + req_Hz[ADG_HZ_48] = 0; + req_Hz[ADG_HZ_441] = 0; for (i = 0; i < req_size; i++) { if (0 == (req_rate[i] % 44100)) - req_441kHz_rate = req_rate[i]; + req_Hz[ADG_HZ_441] = req_rate[i]; if (0 == (req_rate[i] % 48000)) - req_48kHz_rate = req_rate[i]; + req_Hz[ADG_HZ_48] = req_rate[i]; } - if (req_rate[0] % 48000 == 0) - rsnd_flags_set(adg, AUDIO_OUT_48); - - if (of_get_property(np, "clkout-lr-asynchronous", NULL)) - rsnd_flags_set(adg, LRCLK_ASYNC); - /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. @@ -529,47 +537,48 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start() */ - adg->rbga_rate_for_441khz = 0; - adg->rbgb_rate_for_48khz = 0; - for_each_rsnd_clk(clk, adg, i) { + for_each_rsnd_clkin(clk, adg, i) { rate = clk_get_rate(clk); if (0 == rate) /* not used */ continue; /* RBGA */ - if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) { + if (!adg->rbg_rate[ADG_HZ_441] && (0 == rate % 44100)) { div = 6; - if (req_441kHz_rate) - div = rate / req_441kHz_rate; + if (req_Hz[ADG_HZ_441]) + div = rate / req_Hz[ADG_HZ_441]; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbga = rbgx; - adg->rbga_rate_for_441khz = rate / div; + adg->rbg_rate[ADG_HZ_441] = rate / div; ckr |= brg_table[i] << 20; - if (req_441kHz_rate && - !rsnd_flags_has(adg, AUDIO_OUT_48)) + if (req_Hz[ADG_HZ_441]) parent_clk_name = __clk_get_name(clk); } } /* RBGB */ - if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) { + if (!adg->rbg_rate[ADG_HZ_48] && (0 == rate % 48000)) { div = 6; - if (req_48kHz_rate) - div = rate / req_48kHz_rate; + if (req_Hz[ADG_HZ_48]) + div = rate / req_Hz[ADG_HZ_48]; rbgx = rsnd_adg_calculate_rbgx(div); if (BRRx_MASK(rbgx) == rbgx) { rbgb = rbgx; - adg->rbgb_rate_for_48khz = rate / div; + adg->rbg_rate[ADG_HZ_48] = rate / div; ckr |= brg_table[i] << 16; - if (req_48kHz_rate && - rsnd_flags_has(adg, AUDIO_OUT_48)) + if (req_Hz[ADG_HZ_48]) parent_clk_name = __clk_get_name(clk); } } } + clkout_name = clkout_name_gen2; + clkout_size = ARRAY_SIZE(clkout_name_gen2); + if (rsnd_is_gen4(priv)) + clkout_size = 1; /* reuse clkout_name_gen2[] */ + /* * ADG supports BRRA/BRRB output only. * this means all clkout0/1/2/3 will be * same rate @@ -586,13 +595,14 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) goto err; adg->clkout[CLKOUT] = clk; + adg->clkout_size = 1; of_clk_add_provider(np, of_clk_src_simple_get, clk); } /* * for clkout0/1/2/3 */ else { - for (i = 0; i < CLKOUTMAX; i++) { + for (i = 0; i < clkout_size; i++) { clk = clk_register_fixed_rate(dev, clkout_name[i], parent_clk_name, 0, req_rate[0]); @@ -602,7 +612,8 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) adg->clkout[i] = clk; } adg->onecell.clks = adg->clkout; - adg->onecell.clk_num = CLKOUTMAX; + adg->onecell.clk_num = clkout_size; + adg->clkout_size = clkout_size; of_clk_add_provider(np, of_clk_src_onecell_get, &adg->onecell); } @@ -647,22 +658,22 @@ void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m) struct clk *clk; int i; - for_each_rsnd_clk(clk, adg, i) + for_each_rsnd_clkin(clk, adg, i) dbg_msg(dev, m, "%-18s : %pa : %ld\n", __clk_get_name(clk), clk, clk_get_rate(clk)); dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", adg->ckr, adg->rbga, adg->rbgb); - 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); + dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->rbg_rate[ADG_HZ_441]); + dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->rbg_rate[ADG_HZ_48]); /* * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() * by BRGCKR::BRGCKR_31 */ for_each_rsnd_clkout(clk, adg, i) - dbg_msg(dev, m, "clkout %d : %pa : %ld\n", i, - clk, clk_get_rate(clk)); + dbg_msg(dev, m, "%-18s : %pa : %ld\n", + __clk_get_name(clk), clk, clk_get_rate(clk)); } #else #define rsnd_adg_clk_dbg_info(priv, m) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 2d269ac8c137..cb17f7d0cf0c 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -102,6 +102,7 @@ static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 }, { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 }, { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 }, + { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 }, /* Special Handling */ { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) }, {}, @@ -827,6 +828,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, break; default: /* use default */ + /* + * Indicate warning if DT has "dai-tdm-slot-width" + * but the value was not expected. + */ + if (slot_width) + dev_warn(dev, "unsupported TDM slot width (%d), force to use default 32\n", + slot_width); slot_width = 32; } @@ -869,7 +877,8 @@ static unsigned int rsnd_soc_hw_rate_list[] = { static int rsnd_soc_hw_rule(struct rsnd_dai *rdai, unsigned int *list, int list_num, - struct snd_interval *baseline, struct snd_interval *iv) + struct snd_interval *baseline, struct snd_interval *iv, + struct rsnd_dai_stream *io, char *unit) { struct snd_interval p; unsigned int rate; @@ -899,6 +908,16 @@ static int rsnd_soc_hw_rule(struct rsnd_dai *rdai, } } + /* Indicate error once if it can't handle */ + if (!rsnd_flags_has(io, RSND_HW_RULE_ERR) && (p.min > p.max)) { + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "It can't handle %d %s <-> %d %s\n", + baseline->min, unit, baseline->max, unit); + rsnd_flags_set(io, RSND_HW_RULE_ERR); + } + return snd_interval_refine(iv, &p); } @@ -922,7 +941,7 @@ static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list, ARRAY_SIZE(rsnd_soc_hw_rate_list), - &ic, ir); + &ic, ir, io, "ch"); } static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, @@ -945,7 +964,7 @@ static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list, ARRAY_SIZE(rsnd_soc_hw_channels_list), - ir, &ic); + ir, &ic, io, "Hz"); } static const struct snd_pcm_hardware rsnd_pcm_hardware = { @@ -970,6 +989,8 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, unsigned int max_channels = rsnd_rdai_channels_get(rdai); int i; + rsnd_flags_del(io, RSND_HW_RULE_ERR); + rsnd_dai_stream_init(io, substream); /* @@ -1343,6 +1364,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; struct device *dev = rsnd_priv_to_dev(priv); + int playback_exist = 0, capture_exist = 0; int io_i; rdai = rsnd_rdai_get(priv, dai_i); @@ -1357,22 +1379,6 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, drv->ops = &rsnd_soc_dai_ops; drv->pcm_new = rsnd_pcm_new; - snprintf(io_playback->name, RSND_DAI_NAME_SIZE, - "DAI%d Playback", dai_i); - drv->playback.rates = RSND_RATES; - drv->playback.formats = RSND_FMTS; - drv->playback.channels_min = 2; - drv->playback.channels_max = 8; - drv->playback.stream_name = io_playback->name; - - snprintf(io_capture->name, RSND_DAI_NAME_SIZE, - "DAI%d Capture", dai_i); - drv->capture.rates = RSND_RATES; - drv->capture.formats = RSND_FMTS; - drv->capture.channels_min = 2; - drv->capture.channels_max = 8; - drv->capture.stream_name = io_capture->name; - io_playback->rdai = rdai; io_capture->rdai = rdai; rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ @@ -1386,6 +1392,14 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, if (!playback && !capture) break; + if (io_i == 0) { + /* check whether playback/capture property exists */ + if (playback) + playback_exist = 1; + if (capture) + capture_exist = 1; + } + rsnd_parse_connect_ssi(rdai, playback, capture); rsnd_parse_connect_ssiu(rdai, playback, capture); rsnd_parse_connect_src(rdai, playback, capture); @@ -1397,6 +1411,23 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, of_node_put(capture); } + if (playback_exist) { + snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i); + drv->playback.rates = RSND_RATES; + drv->playback.formats = RSND_FMTS; + drv->playback.channels_min = 2; + drv->playback.channels_max = 8; + drv->playback.stream_name = io_playback->name; + } + if (capture_exist) { + snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i); + drv->capture.rates = RSND_RATES; + drv->capture.formats = RSND_FMTS; + drv->capture.channels_min = 2; + drv->capture.channels_max = 8; + drv->capture.stream_name = io_capture->name; + } + if (rsnd_ssi_is_pin_sharing(io_capture) || rsnd_ssi_is_pin_sharing(io_playback)) { /* should have symmetric_rate if pin sharing */ @@ -1444,7 +1475,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) if (is_graph) { for_each_endpoint_of_node(dai_node, dai_np) { __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv)) { + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { rdai = rsnd_rdai_get(priv, dai_i); rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); @@ -1455,7 +1486,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) } else { for_each_child_of_node(dai_node, dai_np) { __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv)) { + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { rdai = rsnd_rdai_get(priv, dai_i); rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 463ab237d7bd..1c494e521463 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -653,22 +653,54 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; } +/* + * Gen4 DMA read/write register offset + * + * ex) R-Car V4H case + * mod / SYS-DMAC in / SYS-DMAC out + * SSI_SDMC: 0xec400000 / 0xec400000 / 0xec400000 + */ +#define RDMA_SSI_SDMC(addr, i) (addr + (0x8000 * i)) +static dma_addr_t +rsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + int is_play, int is_from) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_GEN4_SDMC); + int id = rsnd_mod_id(mod); + int busif = rsnd_mod_id_sub(mod); + + /* + * SSI0 only is supported + */ + if (id != 0) { + struct device *dev = rsnd_priv_to_dev(priv); + + dev_err(dev, "This driver doesn't support non SSI0"); + return -EINVAL; + } + + return RDMA_SSI_SDMC(addr, busif); +} + static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { struct rsnd_priv *priv = rsnd_io_to_priv(io); + if (!mod) + return 0; + /* * gen1 uses default DMA addr */ if (rsnd_is_gen1(priv)) return 0; - - if (!mod) - return 0; - - return rsnd_gen2_dma_addr(io, mod, is_play, is_from); + else if (rsnd_is_gen4(priv)) + return rsnd_gen4_dma_addr(io, mod, is_play, is_from); + else + return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } #define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ @@ -885,19 +917,28 @@ int rsnd_dma_probe(struct rsnd_priv *priv) /* * for Gen2 or later */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); - if (!dmac || !res) { + if (!dmac) { dev_err(dev, "dma allocate failed\n"); return 0; /* it will be PIO mode */ } + /* for Gen4 doesn't have DMA-pp */ + if (rsnd_is_gen4(priv)) + goto audmapp_end; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); + if (!res) { + dev_err(dev, "lack of audmapp in DT\n"); + return 0; /* it will be PIO mode */ + } + dmac->dmapp_num = 0; dmac->ppres = res->start; dmac->ppbase = devm_ioremap_resource(dev, res); if (IS_ERR(dmac->ppbase)) return PTR_ERR(dmac->ppbase); - +audmapp_end: priv->dma = dmac; /* dummy mem mod for debug */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 925565baaa41..86bdecc24956 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -216,6 +216,74 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, } /* + * Gen4 + */ +static int rsnd_gen4_probe(struct rsnd_priv *priv) +{ + static const struct rsnd_regmap_field_conf conf_ssiu[] = { + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890), + RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898), + RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840), + RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848), + RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880), + RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), + + RSND_GEN_S_REG(SSI_BUSIF0_MODE, 0x0), + RSND_GEN_S_REG(SSI_BUSIF0_ADINR, 0x4), + RSND_GEN_S_REG(SSI_BUSIF0_DALIGN, 0x8), + RSND_GEN_S_REG(SSI_BUSIF1_MODE, 0x20), + RSND_GEN_S_REG(SSI_BUSIF1_ADINR, 0x24), + RSND_GEN_S_REG(SSI_BUSIF1_DALIGN, 0x28), + RSND_GEN_S_REG(SSI_BUSIF2_MODE, 0x40), + RSND_GEN_S_REG(SSI_BUSIF2_ADINR, 0x44), + RSND_GEN_S_REG(SSI_BUSIF2_DALIGN, 0x48), + RSND_GEN_S_REG(SSI_BUSIF3_MODE, 0x60), + RSND_GEN_S_REG(SSI_BUSIF3_ADINR, 0x64), + RSND_GEN_S_REG(SSI_BUSIF3_DALIGN, 0x68), + RSND_GEN_S_REG(SSI_BUSIF4_MODE, 0x500), + RSND_GEN_S_REG(SSI_BUSIF4_ADINR, 0x504), + RSND_GEN_S_REG(SSI_BUSIF4_DALIGN, 0x508), + RSND_GEN_S_REG(SSI_BUSIF5_MODE, 0x520), + RSND_GEN_S_REG(SSI_BUSIF5_ADINR, 0x524), + RSND_GEN_S_REG(SSI_BUSIF5_DALIGN, 0x528), + RSND_GEN_S_REG(SSI_BUSIF6_MODE, 0x540), + RSND_GEN_S_REG(SSI_BUSIF6_ADINR, 0x544), + RSND_GEN_S_REG(SSI_BUSIF6_DALIGN, 0x548), + RSND_GEN_S_REG(SSI_BUSIF7_MODE, 0x560), + RSND_GEN_S_REG(SSI_BUSIF7_ADINR, 0x564), + RSND_GEN_S_REG(SSI_BUSIF7_DALIGN, 0x568), + RSND_GEN_S_REG(SSI_CTRL, 0x010), + RSND_GEN_S_REG(SSI_INT_ENABLE, 0x018), + RSND_GEN_S_REG(SSI_MODE, 0x00c), + RSND_GEN_S_REG(SSI_MODE2, 0xa0c), + }; + static const struct rsnd_regmap_field_conf conf_adg[] = { + RSND_GEN_S_REG(BRRA, 0x00), + RSND_GEN_S_REG(BRRB, 0x04), + RSND_GEN_S_REG(BRGCKR, 0x08), + RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), + }; + static const struct rsnd_regmap_field_conf conf_ssi[] = { + RSND_GEN_S_REG(SSICR, 0x00), + RSND_GEN_S_REG(SSISR, 0x04), + RSND_GEN_S_REG(SSITDR, 0x08), + RSND_GEN_S_REG(SSIRDR, 0x0c), + RSND_GEN_S_REG(SSIWSR, 0x20), + }; + static const struct rsnd_regmap_field_conf conf_sdmc[] = { + RSND_GEN_M_REG(SSI_BUSIF, 0x0, 0x8000), + }; + int ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_ADG, "adg", conf_adg); + int ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SSIU, "ssiu", conf_ssiu); + int ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SSI, "ssi", conf_ssi); + int ret_sdmc = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SDMC, "sdmc", conf_sdmc); + + return ret_adg | ret_ssiu | ret_ssi | ret_sdmc; +} + +/* * Gen2 */ static int rsnd_gen2_probe(struct rsnd_priv *priv) @@ -484,6 +552,8 @@ int rsnd_gen_probe(struct rsnd_priv *priv) else if (rsnd_is_gen2(priv) || rsnd_is_gen3(priv)) ret = rsnd_gen2_probe(priv); + else if (rsnd_is_gen4(priv)) + ret = rsnd_gen4_probe(priv); if (ret < 0) dev_err(dev, "unknown generation R-Car sound device\n"); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index d9cd190d7e19..239705d52517 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -31,6 +31,11 @@ #define RSND_GEN2_SSIU 2 #define RSND_GEN2_SSI 3 +#define RSND_GEN4_ADG 0 +#define RSND_GEN4_SSIU 1 +#define RSND_GEN4_SSI 2 +#define RSND_GEN4_SDMC 3 + #define RSND_BASE_MAX 4 /* @@ -197,6 +202,7 @@ enum rsnd_reg { SSI_SYS_INT_ENABLE5, SSI_SYS_INT_ENABLE6, SSI_SYS_INT_ENABLE7, + SSI_BUSIF, HDMI0_SEL, HDMI1_SEL, SSI9_BUSIF0_MODE, @@ -513,6 +519,7 @@ struct rsnd_dai_stream { #define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ #define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ #define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */ +#define RSND_HW_RULE_ERR (1 << 3) /* hw_rule error */ #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) @@ -628,6 +635,7 @@ struct rsnd_priv { #define RSND_GEN1 (1 << 0) #define RSND_GEN2 (2 << 0) #define RSND_GEN3 (3 << 0) +#define RSND_GEN4 (4 << 0) #define RSND_SOC_MASK (0xFF << 4) #define RSND_SOC_E (1 << 4) /* E1/E2/E3 */ @@ -702,6 +710,7 @@ struct rsnd_priv { #define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) #define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3) +#define rsnd_is_gen4(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN4) #define rsnd_is_e3(priv) (((priv)->flags & \ (RSND_GEN_MASK | RSND_SOC_MASK)) == \ (RSND_GEN3 | RSND_SOC_E)) @@ -891,18 +900,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); dev_info(dev, param); \ } while (0) -/* - * If you don't need rsnd_dai_call debug message, - * define RSND_DEBUG_NO_DAI_CALL as 1 on top of core.c - * - * #define RSND_DEBUG_NO_DAI_CALL 1 - */ -#define rsnd_dbg_dai_call(dev, param...) \ - if (!IS_BUILTIN(RSND_DEBUG_NO_DAI_CALL)) \ - 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, @@ -913,3 +910,5 @@ void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod, #else #define rsnd_debugfs_probe NULL #endif + +#endif /* RSND_H */ diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7ade6c5ed96f..8ddee5b03ece 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -303,15 +303,14 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; } + ret = -EIO; main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx); - if (!main_rate) { - dev_err(dev, "unsupported clock rate\n"); - return -EIO; - } + if (!main_rate) + goto rate_err; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (ret < 0) - return ret; + goto rate_err; /* * SSI clock will be output contiguously @@ -333,6 +332,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, rsnd_mod_name(mod), chan, rate); return 0; + +rate_err: + dev_err(dev, "unsupported clock rate\n"); + return ret; } static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 281bc20d4c5d..17bd8cc86dd0 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -29,8 +29,8 @@ struct rsnd_ssiu { i++) /* - * SSI Gen2 Gen3 - * 0 BUSIF0-3 BUSIF0-7 + * SSI Gen2 Gen3 Gen4 + * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7 * 1 BUSIF0-3 BUSIF0-7 * 2 BUSIF0-3 BUSIF0-7 * 3 BUSIF0 BUSIF0-7 @@ -40,10 +40,11 @@ struct rsnd_ssiu { * 7 BUSIF0 BUSIF0 * 8 BUSIF0 BUSIF0 * 9 BUSIF0-3 BUSIF0-7 - * total 22 52 + * total 22 52 8 */ 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 }; +static const int gen4_id[] = { 0 }; /* enable busif buffer over/under run interrupt. */ #define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1) @@ -152,6 +153,10 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, /* clear status */ rsnd_ssiu_busif_err_status_clear(mod); + /* Gen4 doesn't have SSI_MODE */ + if (rsnd_is_gen4(priv)) + goto ssi_mode_setting_end; + /* * SSI_MODE0 */ @@ -206,6 +211,7 @@ 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); +ssi_mode_setting_end: /* * Enable busif buffer over/under run interrupt. * It will be handled from ssi.c @@ -553,6 +559,9 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) } else if (rsnd_is_gen3(priv)) { list = gen3_id; nr = ARRAY_SIZE(gen3_id); + } else if (rsnd_is_gen4(priv)) { + list = gen4_id; + nr = ARRAY_SIZE(gen4_id); } else { dev_err(dev, "unknown SSIU\n"); return -ENODEV; diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 32c5be61e2ec..4e4fe29ade50 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -14,10 +14,9 @@ #include <linux/ctype.h> #include <linux/delay.h> #include <linux/export.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/init.h> -#include <linux/of_gpio.h> #include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/slab.h> @@ -29,9 +28,9 @@ struct snd_ac97_reset_cfg { struct pinctrl_state *pstate_reset; struct pinctrl_state *pstate_warm_reset; struct pinctrl_state *pstate_run; - int gpio_sdata; - int gpio_sync; - int gpio_reset; + struct gpio_desc *reset_gpio; + struct gpio_desc *sdata_gpio; + struct gpio_desc *sync_gpio; }; static struct snd_ac97_bus soc_ac97_bus = { @@ -268,11 +267,11 @@ static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 1); udelay(10); - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0); pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); msleep(2); @@ -284,13 +283,13 @@ static void snd_soc_ac97_reset(struct snd_ac97 *ac97) pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); - gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); - gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); - gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0); + gpiod_direction_output_raw(snd_ac97_rst_cfg.sdata_gpio, 0); + gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 0); udelay(10); - gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 1); pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); msleep(2); @@ -301,8 +300,6 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev, { struct pinctrl *p; struct pinctrl_state *state; - int gpio; - int ret; p = devm_pinctrl_get(dev); if (IS_ERR(p)) { @@ -332,41 +329,20 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev, } cfg->pstate_run = state; - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-sync gpio\n"); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link sync"); - if (ret) { - dev_err(dev, "Failed requesting ac97-sync gpio\n"); - return ret; - } - cfg->gpio_sync = gpio; + cfg->sync_gpio = devm_gpiod_get_index(dev, "ac97", 0, GPIOD_ASIS); + if (IS_ERR(cfg->sync_gpio)) + return dev_err_probe(dev, PTR_ERR(cfg->sync_gpio), "Can't find ac97-sync gpio\n"); + gpiod_set_consumer_name(cfg->sync_gpio, "AC97 link sync"); - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); - if (ret) { - dev_err(dev, "Failed requesting ac97-sdata gpio\n"); - return ret; - } - cfg->gpio_sdata = gpio; + cfg->sdata_gpio = devm_gpiod_get_index(dev, "ac97", 1, GPIOD_ASIS); + if (IS_ERR(cfg->sdata_gpio)) + return dev_err_probe(dev, PTR_ERR(cfg->sdata_gpio), "Can't find ac97-sdata gpio\n"); + gpiod_set_consumer_name(cfg->sdata_gpio, "AC97 link sdata"); - gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); - if (gpio < 0) { - dev_err(dev, "Can't find ac97-reset gpio\n"); - return gpio; - } - ret = devm_gpio_request(dev, gpio, "AC97 link reset"); - if (ret) { - dev_err(dev, "Failed requesting ac97-reset gpio\n"); - return ret; - } - cfg->gpio_reset = gpio; + cfg->reset_gpio = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_ASIS); + if (IS_ERR(cfg->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(cfg->reset_gpio), "Can't find ac97-reset gpio\n"); + gpiod_set_consumer_name(cfg->reset_gpio, "AC97 link reset"); return 0; } diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index e12f8244242b..3cd6952212e1 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -256,6 +256,26 @@ int snd_soc_component_set_jack(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); +/** + * snd_soc_component_get_jack_type + * @component: COMPONENTs + * + * Returns the jack type of the component + * This can either be the supported type or one read from + * devicetree with the property: jack-type. + */ +int snd_soc_component_get_jack_type( + struct snd_soc_component *component) +{ + int ret = -ENOTSUPP; + + if (component->driver->get_jack_type) + ret = component->driver->get_jack_type(component); + + return soc_component_ret(component, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_component_get_jack_type); + int snd_soc_component_module_get(struct snd_soc_component *component, void *mark, int upon_open) { diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 870f13e1d389..e7aa6f360cab 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -149,6 +149,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (ret < 0) goto be_err; + mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); + /* calculate valid and active FE <-> BE dpcms */ dpcm_process_paths(fe, stream, &list, 1); fe->dpcm[stream].runtime = fe_substream->runtime; @@ -184,7 +186,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; - mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); snd_soc_runtime_activate(fe, stream); mutex_unlock(&fe->card->pcm_mutex); @@ -215,7 +216,6 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); snd_soc_runtime_deactivate(fe, stream); - mutex_unlock(&fe->card->pcm_mutex); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; @@ -234,6 +234,8 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) dpcm_be_disconnect(fe, stream); + mutex_unlock(&fe->card->pcm_mutex); + fe->dpcm[stream].runtime = NULL; snd_soc_link_compr_shutdown(cstream, 0); @@ -409,8 +411,9 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, ret = snd_soc_link_compr_set_params(cstream); if (ret < 0) goto out; - + mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + mutex_unlock(&fe->card->pcm_mutex); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; out: @@ -623,7 +626,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) rtd->fe_compr = 1; if (rtd->dai_link->dpcm_playback) be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; - else if (rtd->dai_link->dpcm_capture) + if (rtd->dai_link->dpcm_capture) be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); } else { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e613698824fd..71b022f7edfd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -553,7 +553,7 @@ int snd_soc_suspend(struct device *dev) int i; /* If the card is not initialized yet there is nothing to do */ - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* @@ -695,7 +695,7 @@ int snd_soc_resume(struct device *dev) struct snd_soc_component *component; /* If the card is not initialized yet there is nothing to do */ - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* activate pins from sleep state */ @@ -1915,7 +1915,7 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card) static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) { - if (card->instantiated) { + if (snd_soc_card_is_instantiated(card)) { card->instantiated = false; snd_soc_flush_all_delayed_work(card); @@ -2126,7 +2126,7 @@ int snd_soc_poweroff(struct device *dev) struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_component *component; - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 29a75fdf90e0..0119afbd01fc 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -267,6 +267,11 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { int ret = -ENOTSUPP; + int stream; + unsigned int *tdm_mask[] = { + &tx_mask, + &rx_mask, + }; if (dai->driver->ops && dai->driver->ops->xlate_tdm_slot_mask) @@ -275,8 +280,8 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, else snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - dai->tx_mask = tx_mask; - dai->rx_mask = rx_mask; + for_each_pcm_streams(stream) + snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); if (dai->driver->ops && dai->driver->ops->set_tdm_slot) @@ -509,7 +514,7 @@ void snd_soc_dai_action(struct snd_soc_dai *dai, int stream, int action) { /* see snd_soc_dai_stream_active() */ - dai->stream_active[stream] += action; + dai->stream[stream].active += action; /* see snd_soc_component_active() */ dai->component->active += action; @@ -522,7 +527,7 @@ int snd_soc_dai_active(struct snd_soc_dai *dai) active = 0; for_each_pcm_streams(stream) - active += dai->stream_active[stream]; + active += dai->stream[stream].active; return active; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f61c8633e7eb..5d9a671e50f1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -149,7 +149,7 @@ static int dapm_down_seq[] = { static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) { - if (dapm->card && dapm->card->instantiated) + if (snd_soc_card_is_instantiated(dapm->card)) lockdep_assert_held(&dapm->card->dapm_mutex); } @@ -1297,7 +1297,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, enum snd_soc_dapm_direction)) { struct snd_soc_card *card = dai->component->card; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); LIST_HEAD(widgets); int paths; int ret; @@ -1305,12 +1305,10 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); paths = is_connected_output_ep(w, &widgets, custom_stop_condition); } else { - w = dai->capture_widget; invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); paths = is_connected_input_ep(w, &widgets, custom_stop_condition); @@ -2614,7 +2612,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. */ - if (!dapm->card || !dapm->card->instantiated) + if (!snd_soc_card_is_instantiated(dapm->card)) return 0; return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); @@ -2908,7 +2906,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(path->node[dir], "Route added"); } - if (dapm->card->instantiated && path->connect) + if (snd_soc_card_is_instantiated(dapm->card) && path->connect) dapm_path_invalidate(path); return 0; @@ -4229,7 +4227,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, return PTR_ERR(w); w->priv = dai; - dai->playback_widget = w; + snd_soc_dai_set_widget_playback(dai, w); } if (dai->driver->capture.stream_name) { @@ -4245,7 +4243,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, return PTR_ERR(w); w->priv = dai; - dai->capture_widget = w; + snd_soc_dai_set_widget_capture(dai, w); } return 0; @@ -4339,16 +4337,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, int stream; if (dai_link->params) { - playback_cpu = cpu_dai->capture_widget; - capture_cpu = cpu_dai->playback_widget; + playback_cpu = snd_soc_dai_get_widget_capture(cpu_dai); + capture_cpu = snd_soc_dai_get_widget_playback(cpu_dai); } else { - playback_cpu = cpu_dai->playback_widget; - capture_cpu = cpu_dai->capture_widget; + playback_cpu = snd_soc_dai_get_widget_playback(cpu_dai); + capture_cpu = snd_soc_dai_get_widget_capture(cpu_dai); } /* connect BE DAI playback if widgets are valid */ stream = SNDRV_PCM_STREAM_PLAYBACK; - codec = codec_dai->playback_widget; + codec = snd_soc_dai_get_widget(codec_dai, stream); if (playback_cpu && codec) { if (dai_link->params && !rtd->c2c_widget[stream]) { @@ -4367,7 +4365,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, capture: /* connect BE DAI capture if widgets are valid */ stream = SNDRV_PCM_STREAM_CAPTURE; - codec = codec_dai->capture_widget; + codec = snd_soc_dai_get_widget(codec_dai, stream); if (codec && capture_cpu) { if (dai_link->params && !rtd->c2c_widget[stream]) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 579a44d81d9a..005b179a770a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1012,6 +1012,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, for_each_rtd_codec_dais(rtd, i, codec_dai) { struct snd_pcm_hw_params codec_params; + unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); /* * Skip CODECs which don't support the current stream type, @@ -1034,15 +1035,8 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, codec_params = *params; /* fixup params based on TDM slot masks */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - codec_dai->tx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->tx_mask); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && - codec_dai->rx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->rx_mask); + if (tdm_mask) + soc_pcm_codec_params_fixup(&codec_params, tdm_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, &codec_params); @@ -1337,7 +1331,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, return NULL; } -static int widget_in_list(struct snd_soc_dapm_widget_list *list, +int widget_in_list(struct snd_soc_dapm_widget_list *list, struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; @@ -1349,6 +1343,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } +EXPORT_SYMBOL_GPL(widget_in_list); bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir) { diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index a79a2fb260b8..07421f5d4ebd 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -44,9 +44,8 @@ #define SOC_TPLG_PASS_WIDGET 3 #define SOC_TPLG_PASS_PCM_DAI 4 #define SOC_TPLG_PASS_GRAPH 5 -#define SOC_TPLG_PASS_PINS 6 -#define SOC_TPLG_PASS_BE_DAI 7 -#define SOC_TPLG_PASS_LINK 8 +#define SOC_TPLG_PASS_BE_DAI 6 +#define SOC_TPLG_PASS_LINK 7 #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST #define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK @@ -77,9 +76,6 @@ struct soc_tplg { struct snd_soc_tplg_ops *ops; }; -static int soc_tplg_process_headers(struct soc_tplg *tplg); -static int soc_tplg_complete(struct soc_tplg *tplg); - /* check we dont overflow the data for this control chunk */ static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, unsigned int count, size_t bytes, const char *elem_type) @@ -186,7 +182,7 @@ static const struct soc_tplg_map dapm_map[] = { {SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder}, }; -static int tplc_chan_get_reg(struct soc_tplg *tplg, +static int tplg_chan_get_reg(struct soc_tplg *tplg, struct snd_soc_tplg_channel *chan, int map) { int i; @@ -199,7 +195,7 @@ static int tplc_chan_get_reg(struct soc_tplg *tplg, return -EINVAL; } -static int tplc_chan_get_shift(struct soc_tplg *tplg, +static int tplg_chan_get_shift(struct soc_tplg *tplg, struct snd_soc_tplg_channel *chan, int map) { int i; @@ -354,69 +350,37 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg, tplg->dev, k, comp->name_prefix, comp, kcontrol); } -/* remove a mixer kcontrol */ -static void remove_mixer(struct snd_soc_component *comp, - struct snd_soc_dobj *dobj, int pass) -{ - struct snd_card *card = comp->card->snd_card; - - if (pass != SOC_TPLG_PASS_CONTROL) - return; - - if (dobj->ops && dobj->ops->control_unload) - dobj->ops->control_unload(comp, dobj); - - snd_ctl_remove(card, dobj->control.kcontrol); - list_del(&dobj->list); -} - -/* remove an enum kcontrol */ -static void remove_enum(struct snd_soc_component *comp, - struct snd_soc_dobj *dobj, int pass) -{ - struct snd_card *card = comp->card->snd_card; - - if (pass != SOC_TPLG_PASS_CONTROL) - return; - - if (dobj->ops && dobj->ops->control_unload) - dobj->ops->control_unload(comp, dobj); - - snd_ctl_remove(card, dobj->control.kcontrol); - list_del(&dobj->list); -} - -/* remove a byte kcontrol */ -static void remove_bytes(struct snd_soc_component *comp, - struct snd_soc_dobj *dobj, int pass) +/* remove kcontrol */ +static void soc_tplg_remove_kcontrol(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, + int pass) { struct snd_card *card = comp->card->snd_card; if (pass != SOC_TPLG_PASS_CONTROL) return; - if (dobj->ops && dobj->ops->control_unload) - dobj->ops->control_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); snd_ctl_remove(card, dobj->control.kcontrol); list_del(&dobj->list); } /* remove a route */ -static void remove_route(struct snd_soc_component *comp, +static void soc_tplg_remove_route(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { if (pass != SOC_TPLG_PASS_GRAPH) return; - if (dobj->ops && dobj->ops->dapm_route_unload) - dobj->ops->dapm_route_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); list_del(&dobj->list); } /* remove a widget and it's kcontrols - routes must be removed first */ -static void remove_widget(struct snd_soc_component *comp, +static void soc_tplg_remove_widget(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_card *card = comp->card->snd_card; @@ -427,8 +391,8 @@ static void remove_widget(struct snd_soc_component *comp, if (pass != SOC_TPLG_PASS_WIDGET) return; - if (dobj->ops && dobj->ops->widget_unload) - dobj->ops->widget_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); if (!w->kcontrols) goto free_news; @@ -444,7 +408,7 @@ free_news: } /* remove DAI configurations */ -static void remove_dai(struct snd_soc_component *comp, +static void soc_tplg_remove_dai(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_driver *dai_drv = @@ -454,8 +418,8 @@ static void remove_dai(struct snd_soc_component *comp, if (pass != SOC_TPLG_PASS_PCM_DAI) return; - if (dobj->ops && dobj->ops->dai_unload) - dobj->ops->dai_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); for_each_component_dais_safe(comp, dai, _dai) if (dai->driver == dai_drv) @@ -465,7 +429,7 @@ static void remove_dai(struct snd_soc_component *comp, } /* remove link configurations */ -static void remove_link(struct snd_soc_component *comp, +static void soc_tplg_remove_link(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { struct snd_soc_dai_link *link = @@ -474,8 +438,8 @@ static void remove_link(struct snd_soc_component *comp, if (pass != SOC_TPLG_PASS_PCM_DAI) return; - if (dobj->ops && dobj->ops->link_unload) - dobj->ops->link_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); list_del(&dobj->list); snd_soc_remove_pcm_runtime(comp->card, @@ -489,11 +453,11 @@ static void remove_backend_link(struct snd_soc_component *comp, if (pass != SOC_TPLG_PASS_LINK) return; - if (dobj->ops && dobj->ops->link_unload) - dobj->ops->link_unload(comp, dobj); + if (dobj->unload) + dobj->unload(comp, dobj); /* - * We don't free the link here as what remove_link() do since BE + * We don't free the link here as what soc_tplg_remove_link() do since BE * links are not allocated by topology. * We however need to reset the dobj type to its initial values */ @@ -714,7 +678,8 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) sbe->max = le32_to_cpu(be->max); sbe->dobj.type = SND_SOC_DOBJ_BYTES; - sbe->dobj.ops = tplg->ops; + if (tplg->ops) + sbe->dobj.unload = tplg->ops->control_unload; INIT_LIST_HEAD(&sbe->dobj.list); /* map io handlers */ @@ -725,7 +690,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) } /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be); + ret = soc_tplg_control_load(tplg, &kc, &be->hdr); if (ret < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name); goto err; @@ -780,18 +745,19 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) 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->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); + sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rshift = tplg_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; - sm->dobj.ops = tplg->ops; sm->dobj.type = SND_SOC_DOBJ_MIXER; + if (tplg->ops) + sm->dobj.unload = tplg->ops->control_unload; INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ @@ -809,7 +775,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) } /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc); + ret = soc_tplg_control_load(tplg, &kc, &mc->hdr); if (ret < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name); goto err; @@ -927,16 +893,17 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; kc.access = le32_to_cpu(ec->hdr.access); - se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplc_chan_get_shift(tplg, ec->channel, + se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); se->mask = le32_to_cpu(ec->mask); se->dobj.index = tplg->index; se->dobj.type = SND_SOC_DOBJ_ENUM; - se->dobj.ops = tplg->ops; + if (tplg->ops) + se->dobj.unload = tplg->ops->control_unload; INIT_LIST_HEAD(&se->dobj.list); switch (le32_to_cpu(ec->hdr.ops.info)) { @@ -977,7 +944,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) } /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec); + ret = soc_tplg_control_load(tplg, &kc, &ec->hdr); if (ret < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); goto err; @@ -1113,7 +1080,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, /* add route dobj to dobj_list */ route->dobj.type = SND_SOC_DOBJ_GRAPH; - route->dobj.ops = tplg->ops; + if (tplg->ops) + route->dobj.unload = tplg->ops->dapm_route_unload; route->dobj.index = tplg->index; list_add(&route->dobj.list, &tplg->comp->dobj_list); @@ -1161,13 +1129,13 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ 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, + sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rreg = tplc_chan_get_reg(tplg, mc->channel, + sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); - sm->shift = tplc_chan_get_shift(tplg, mc->channel, + sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); - sm->rshift = tplc_chan_get_shift(tplg, mc->channel, + sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); sm->max = le32_to_cpu(mc->max); @@ -1193,7 +1161,7 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc); + err = soc_tplg_control_load(tplg, kc, &mc->hdr); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name); @@ -1233,10 +1201,10 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k kc->access = le32_to_cpu(ec->hdr.access); /* 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, + se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); se->items = le32_to_cpu(ec->items); @@ -1277,7 +1245,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec); + err = soc_tplg_control_load(tplg, kc, &ec->hdr); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); @@ -1329,7 +1297,7 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ } /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be); + err = soc_tplg_control_load(tplg, kc, &be->hdr); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name); @@ -1472,7 +1440,7 @@ widget: /* card dapm mutex is held by the core if we are loading topology * data during sound card init. */ - if (card->instantiated) + if (snd_soc_card_is_instantiated(card)) widget = snd_soc_dapm_new_control(dapm, &template); else widget = snd_soc_dapm_new_control_unlocked(dapm, &template); @@ -1483,7 +1451,8 @@ widget: widget->dobj.type = SND_SOC_DOBJ_WIDGET; widget->dobj.widget.kcontrol_type = kcontrol_type; - widget->dobj.ops = tplg->ops; + if (tplg->ops) + widget->dobj.unload = tplg->ops->widget_unload; widget->dobj.index = tplg->index; list_add(&widget->dobj.list, &tplg->comp->dobj_list); @@ -1497,7 +1466,7 @@ widget: return 0; ready_err: - remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET); + soc_tplg_remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET); snd_soc_dapm_free_widget(widget); hdr_err: kfree(template.sname); @@ -1560,7 +1529,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) /* Card might not have been registered at this point. * If so, just return success. */ - if (!card || !card->instantiated) { + if (!snd_soc_card_is_instantiated(card)) { dev_warn(tplg->dev, "ASoC: Parent card not yet available," " widget card binding deferred\n"); return 0; @@ -1571,7 +1540,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", ret); - return 0; + return ret; } static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream, @@ -1661,8 +1630,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, } dai_drv->dobj.index = tplg->index; - dai_drv->dobj.ops = tplg->ops; dai_drv->dobj.type = SND_SOC_DOBJ_PCM; + if (tplg->ops) + dai_drv->dobj.unload = tplg->ops->dai_unload; list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); /* register the DAI to the component */ @@ -1731,8 +1701,9 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, link->num_platforms = 1; link->dobj.index = tplg->index; - link->dobj.ops = tplg->ops; link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + if (tplg->ops) + link->dobj.unload = tplg->ops->link_unload; if (strlen(pcm->pcm_name)) { link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); @@ -2139,8 +2110,9 @@ static int soc_tplg_link_config(struct soc_tplg *tplg, /* for unloading it in snd_soc_tplg_component_remove */ link->dobj.index = tplg->index; - link->dobj.ops = tplg->ops; link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK; + if (tplg->ops) + link->dobj.unload = tplg->ops->link_unload; list_add(&link->dobj.list, &tplg->comp->dobj_list); return 0; @@ -2394,12 +2366,9 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, } /* validate header magic, size and type */ -static int soc_valid_header(struct soc_tplg *tplg, +static int soc_tplg_valid_header(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size) - return 0; - if (le32_to_cpu(hdr->size) != sizeof(*hdr)) { dev_err(tplg->dev, "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n", @@ -2408,7 +2377,7 @@ static int soc_valid_header(struct soc_tplg *tplg, return -EINVAL; } - if (soc_tplg_get_hdr_offset(tplg) + hdr->payload_size >= tplg->fw->size) { + if (soc_tplg_get_hdr_offset(tplg) + le32_to_cpu(hdr->payload_size) >= tplg->fw->size) { dev_err(tplg->dev, "ASoC: invalid header of type %d at offset %ld payload_size %d\n", le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), @@ -2450,7 +2419,7 @@ static int soc_valid_header(struct soc_tplg *tplg, return -EINVAL; } - return 1; + return 0; } /* check header type and call appropriate handler */ @@ -2531,13 +2500,11 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg) while (!soc_tplg_is_eof(tplg)) { /* make sure header is valid before loading */ - ret = soc_valid_header(tplg, hdr); + ret = soc_tplg_valid_header(tplg, hdr); if (ret < 0) { dev_err(tplg->dev, "ASoC: topology: invalid header: %d\n", ret); return ret; - } else if (ret == 0) { - break; } /* load the header object */ @@ -2631,26 +2598,22 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) list) { switch (dobj->type) { - case SND_SOC_DOBJ_MIXER: - remove_mixer(comp, dobj, pass); - break; - case SND_SOC_DOBJ_ENUM: - remove_enum(comp, dobj, pass); - break; case SND_SOC_DOBJ_BYTES: - remove_bytes(comp, dobj, pass); + case SND_SOC_DOBJ_ENUM: + case SND_SOC_DOBJ_MIXER: + soc_tplg_remove_kcontrol(comp, dobj, pass); break; case SND_SOC_DOBJ_GRAPH: - remove_route(comp, dobj, pass); + soc_tplg_remove_route(comp, dobj, pass); break; case SND_SOC_DOBJ_WIDGET: - remove_widget(comp, dobj, pass); + soc_tplg_remove_widget(comp, dobj, pass); break; case SND_SOC_DOBJ_PCM: - remove_dai(comp, dobj, pass); + soc_tplg_remove_dai(comp, dobj, pass); break; case SND_SOC_DOBJ_DAI_LINK: - remove_link(comp, dobj, pass); + soc_tplg_remove_link(comp, dobj, pass); break; case SND_SOC_DOBJ_BACKEND_LINK: /* diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index de5726251dc6..920155dee819 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -85,4 +85,8 @@ #define ACP_SCRATCH_REG_0 0x10000 #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 + +/* Cache window registers */ +#define ACP_DSP0_CACHE_OFFSET0 0x0420 +#define ACP_DSP0_CACHE_SIZE0 0x0424 #endif diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 5a02753c4610..4e0c48a36159 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -200,14 +200,15 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) } EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON); -int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, void *p, size_t sz) { unsigned int offset = sdev->dsp_box.offset; - if (!substream || !sdev->stream_box.size) { + if (!sps || !sdev->stream_box.size) { acp_mailbox_read(sdev, offset, p, sz); } else { + struct snd_pcm_substream *substream = sps->substream; struct acp_dsp_stream *stream = substream->runtime->private_data; if (!stream) @@ -221,9 +222,10 @@ int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *sub EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON); int acp_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset) { + struct snd_pcm_substream *substream = sps->substream; struct acp_dsp_stream *stream = substream->runtime->private_data; /* check for unaligned offset or overflow */ diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index 090c8b18c83c..a4bce5a3ae48 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -151,6 +151,7 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); struct acp_dev_data *adata; unsigned int src_addr, size_fw; u32 page_count, dma_size; @@ -183,6 +184,12 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) if (ret < 0) dev_err(sdev->dev, "acp dma transfer status: %d\n", ret); + if (desc->rev > 3) { + /* Cache Window enable */ + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0, desc->sram_pte_offset); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0, SRAM1_SIZE | BIT(31)); + } + /* Free memory once DMA is complete */ dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 09e16ef8afa0..39165ebf684b 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -12,6 +12,7 @@ #define __SOF_AMD_ACP_H #include "../sof-priv.h" +#include "../sof-audio.h" #define ACP_MAX_STREAM 8 @@ -72,6 +73,8 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define AMD_STACK_DUMP_SIZE 32 +#define SRAM1_SIZE 0x13A000 + enum clock_source { ACP_CLOCK_96M = 0, ACP_CLOCK_48M, @@ -209,10 +212,10 @@ int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_ty /* IPC callbacks */ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context); -int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, void *p, size_t sz); int acp_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset); int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 8e1a9ba111ad..8d205eb16d2f 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -10,6 +10,7 @@ #include "sof-audio.h" #include "sof-priv.h" #include "sof-utils.h" +#include "ops.h" static void sof_set_transferred_bytes(struct sof_compr_stream *sstream, u64 host_pos, u64 buffer_size) @@ -237,6 +238,14 @@ static int sof_compr_set_params(struct snd_soc_component *component, goto out; } + ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[cstream->direction], + ipc_params_reply.posn_offset); + if (ret < 0) { + dev_err(component->dev, "Invalid stream data offset for Compr %d\n", + spcm->pcm.pcm_id); + goto out; + } + sstream->sampling_rate = params->codec.sample_rate; sstream->channels = params->codec.ch_out; sstream->sample_container_bytes = pcm->params.sample_container_bytes; diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index e0e9efd25d34..75e13f4fd1eb 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -22,9 +22,9 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->volume_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get) return tplg_ops->control->volume_get(scontrol, ucontrol); return 0; @@ -37,9 +37,9 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->volume_put) + if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put) return tplg_ops->control->volume_put(scontrol, ucontrol); return false; @@ -74,9 +74,9 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->switch_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get) return tplg_ops->control->switch_get(scontrol, ucontrol); return 0; @@ -89,9 +89,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->switch_put) + if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put) return tplg_ops->control->switch_put(scontrol, ucontrol); return false; @@ -104,9 +104,9 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->enum_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get) return tplg_ops->control->enum_get(scontrol, ucontrol); return 0; @@ -119,9 +119,9 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->enum_put) + if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put) return tplg_ops->control->enum_put(scontrol, ucontrol); return false; @@ -134,9 +134,9 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->bytes_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get) return tplg_ops->control->bytes_get(scontrol, ucontrol); return 0; @@ -149,9 +149,9 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->bytes_put) + if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put) return tplg_ops->control->bytes_put(scontrol, ucontrol); return 0; @@ -165,13 +165,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* make sure we have at least a header */ if (size < sizeof(struct snd_ctl_tlv)) return -EINVAL; - if (tplg_ops->control->bytes_ext_put) + if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_put) return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size); return 0; @@ -184,7 +184,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); int ret, err; ret = pm_runtime_resume_and_get(scomp->dev); @@ -193,7 +193,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ return ret; } - if (tplg_ops->control->bytes_ext_volatile_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get) ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); pm_runtime_mark_last_busy(scomp->dev); @@ -212,9 +212,9 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->bytes_ext_get) + if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get) return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size); return 0; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 625977a29d8a..7de8673a01ce 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -362,6 +362,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->first_boot = true; dev_set_drvdata(dev, sdev); + if (sof_core_debug) + dev_info(dev, "sof_debug value: %#x\n", sof_core_debug); + /* check IPC support */ if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", @@ -387,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); + INIT_LIST_HEAD(&sdev->pipeline_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 36a0e2bf30ff..715ba8a7f2f8 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -322,8 +322,8 @@ config SND_SOC_SOF_HDA_PROBES config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE tristate - select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE - select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE + select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n + select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE != n config SND_SOC_SOF_INTEL_SOUNDWIRE tristate "SOF support for SoundWire" diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 6b075bbe5bfb..a08a77fa946b 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -280,6 +280,8 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY); + hda_dsp_ipc4_schedule_d0i3_work(hdev, msg); + return 0; } diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index a642c3067ec5..8d9c38d562d3 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, { struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; struct snd_soc_pcm_runtime *rtd; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; @@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, w = snd_soc_dai_get_widget(dai, substream->stream); swidget = w->dobj.private; + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_stream_start(hext_stream); + if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + SOF_IPC4_PIPE_PAUSED); + if (ret < 0) + return ret; + pipeline->state = SOF_IPC4_PIPE_PAUSED; + } + + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + SOF_IPC4_PIPE_RUNNING); + if (ret < 0) + return ret; + pipeline->state = SOF_IPC4_PIPE_RUNNING; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: { - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) @@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, } case SNDRV_PCM_TRIGGER_PAUSE_PUSH: { - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) @@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = { .shutdown = ssp_dai_shutdown, }; -static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream) -{ - struct snd_sof_widget *pipe_widget; - struct sof_ipc4_pipeline *pipeline; - struct snd_sof_widget *swidget; - struct snd_soc_dapm_widget *w; - struct snd_sof_dev *sdev; - int ret; - - w = snd_soc_dai_get_widget(dai, stream); - swidget = w->dobj.private; - pipe_widget = swidget->pipe_widget; - pipeline = pipe_widget->private; - sdev = snd_soc_component_get_drvdata(swidget->scomp); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, - SOF_IPC4_PIPE_PAUSED); - if (ret < 0) - return ret; - pipeline->state = SOF_IPC4_PIPE_PAUSED; - - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, - SOF_IPC4_PIPE_RESET); - if (ret < 0) - return ret; - pipeline->state = SOF_IPC4_PIPE_RESET; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, - SOF_IPC4_PIPE_PAUSED); - if (ret < 0) - return ret; - pipeline->state = SOF_IPC4_PIPE_PAUSED; - break; - default: - break; - } - - return 0; -} - -static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - return ipc4_be_dai_common_trigger(dai, cmd, substream->stream); -} - -static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = { - .trigger = ipc4_be_dai_trigger, -}; - -static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = { - .trigger = ipc4_be_dai_trigger, -}; - void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { int i; @@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) struct sof_ipc4_fw_data *ipc4_data = sdev->private; for (i = 0; i < ops->num_drv; i++) { - if (strstr(ops->drv[i].name, "DMIC")) { - ops->drv[i].ops = &ipc4_dmic_dai_ops; - continue; - } - if (strstr(ops->drv[i].name, "SSP")) { - ops->drv[i].ops = &ipc4_ssp_dai_ops; - continue; - } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) if (strstr(ops->drv[i].name, "iDisp") || strstr(ops->drv[i].name, "Analog") || @@ -804,9 +749,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) if (!hda_use_tplg_nhlt) ipc4_data->nhlt = intel_nhlt_init(sdev->dev); - if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)) - sdw_callback.trigger = ipc4_be_dai_common_trigger; - break; } default: diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index b4eacae8564c..68eb06f13a1f 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -364,19 +364,12 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev) static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags) { - struct sof_ipc_pm_gate pm_gate; - struct sof_ipc_reply reply; + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - memset(&pm_gate, 0, sizeof(pm_gate)); + if (pm_ops && pm_ops->set_pm_gate) + return pm_ops->set_pm_gate(sdev, flags); - /* configure pm_gate ipc message */ - pm_gate.hdr.size = sizeof(pm_gate); - pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE; - pm_gate.flags = flags; - - /* send pm_gate ipc to dsp */ - return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate), - &reply, sizeof(reply)); + return 0; } static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) @@ -412,6 +405,34 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) return 0; } +/* + * d0i3 streaming is enabled if all the active streams can + * work in d0i3 state and playback is enabled + */ +static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) +{ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + bool playback_active = false; + int dir; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + substream = spcm->stream[dir].substream; + if (!substream || !substream->runtime) + continue; + + if (!spcm->stream[dir].d0i3_compatible) + return false; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + playback_active = true; + } + } + + return playback_active; +} + static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { @@ -453,6 +474,9 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, !hda_enable_trace_D0I3_S0 || sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; + + if (hda_dsp_d0i3_streaming_applicable(sdev)) + flags |= HDA_PM_PG_STREAMING; } else { /* prevent power gating in D0I0 */ flags = HDA_PM_PPG; diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index a7c454e03952..df541b22b2d2 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -67,6 +67,32 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } +static inline bool hda_dsp_ipc4_pm_msg(u32 primary) +{ + /* pm setting is only supported by module msg */ + if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG) + return false; + + if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX || + SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX) + return true; + + return false; +} + +void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev, + struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc4_msg *msg_data = msg->msg_data; + + /* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */ + if (hda_dsp_ipc4_pm_msg(msg_data->primary)) + return; + + mod_delayed_work(system_wq, &hdev->d0i3_work, + msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS)); +} + int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; @@ -88,6 +114,8 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, msg_data->primary | HDA_DSP_REG_HIPCI_BUSY); + hda_dsp_ipc4_schedule_d0i3_work(hdev, msg); + return 0; } @@ -361,12 +389,13 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) } int hda_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz) { - if (!substream || !sdev->stream_box.size) { + if (!sps || !sdev->stream_box.size) { sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); } else { + struct snd_pcm_substream *substream = sps->substream; struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_stream *hda_stream; @@ -385,9 +414,10 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev, } int hda_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset) { + struct snd_pcm_substream *substream = sps->substream; struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_stream *hda_stream; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index caccaf8fba9c..45f9d4248f14 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -656,10 +656,10 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, int enable, u32 size); int hda_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz); int hda_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset); /* @@ -919,6 +919,8 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context); int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context); bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev); +void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev, + struct snd_sof_ipc_msg *msg); int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void hda_ipc4_dump(struct snd_sof_dev *sdev); extern struct sdw_intel_ops sdw_callback; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 904ae42534e1..307faad2ecf4 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -110,6 +110,8 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR, msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY); + hda_dsp_ipc4_schedule_d0i3_work(hdev, msg); + return 0; } @@ -581,6 +583,18 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } +static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + u32 llp_l, llp_u; + + llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index)); + llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index)); + return ((u64)llp_u << 32) | llp_l; +} + /* Meteorlake ops */ struct snd_sof_dsp_ops sof_mtl_ops; EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -619,6 +633,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) /* dsp core get/put */ /* TODO: add core_get and core_put */ + sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; + sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 0fd4e6fe09b8..26418fb08807 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -6,6 +6,12 @@ * Copyright(c) 2020-2022 Intel Corporation. All rights reserved. */ +/* HDA Registers */ +#define MTL_PPLCLLPL_BASE 0x948 +#define MTL_PPLCLLPU_STRIDE 0x10 +#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE) +#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE) + /* DSP Registers */ #define MTL_HFDSSCS 0x1000 #define MTL_HFDSSCS_SPA_MASK BIT(16) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 3fdc0d854e65..217ac5501a98 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -12,7 +12,8 @@ #include "ipc3-priv.h" /* IPC set()/get() for kcontrols. */ -static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) +static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, + bool set, bool lock) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; @@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool struct snd_sof_widget *swidget; bool widget_found = false; u32 ipc_cmd, msg_bytes; + int ret = 0; list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { @@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool return -EINVAL; } + if (lock) + mutex_lock(&swidget->setup_mutex); + else + lockdep_assert_held(&swidget->setup_mutex); + /* - * Volatile controls should always be part of static pipelines and the widget use_count - * would always be > 0 in this case. For the others, just return the cached value if the - * widget is not set up. + * Volatile controls should always be part of static pipelines and the + * widget use_count would always be > 0 in this case. For the others, + * just return the cached value if the widget is not set up. */ if (!swidget->use_count) - return 0; + goto unlock; /* * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the @@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool sizeof(struct sof_abi_hdr); break; default: - return -EINVAL; + ret = -EINVAL; + goto unlock; } cdata->rhdr.hdr.size = msg_bytes; cdata->elems_remaining = 0; - return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + +unlock: + if (lock) + mutex_unlock(&swidget->setup_mutex); + + return ret; } static void snd_sof_refresh_control(struct snd_sof_control *scontrol) @@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); if (ret < 0) { dev_err(scomp->dev, "Failed to get control data: %d\n", ret); @@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) { - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set mixer updates for %s\n", @@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) { - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set mixer updates for %s\n", @@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) { - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set enum updates for %s\n", @@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - return sof_ipc3_set_get_kcontrol_data(scontrol, true); + return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); return 0; } @@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - return sof_ipc3_set_get_kcontrol_data(scontrol, true); + return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); return 0; } @@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); if (ret < 0) return ret; @@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, list_for_each_entry(scontrol, &sdev->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { /* set kcontrol data in DSP */ - ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); if (ret < 0) { dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n", @@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, if (swidget->dynamic_pipeline_widget) continue; - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); if (ret < 0) dev_warn(sdev->dev, "kcontrol %d read failed for widget %s\n", diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index f10bfc9bd5cb..b29d93e0d216 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -129,7 +129,8 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, return ret; } - ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset); + ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream], + ipc_params_reply.posn_offset); if (ret < 0) { dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", __func__, spcm->pcm.pcm_id); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index b94cc40485ed..dceb78bfe17c 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1651,6 +1651,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata; + size_t priv_size_check; int ret; if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) { @@ -1694,8 +1695,10 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ goto err; } - if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) { - dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n"); + priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr); + if (priv_size_check != scontrol->priv_size) { + dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", + priv_size_check, scontrol->priv_size); ret = -EINVAL; goto err; } @@ -2230,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } - swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget); - if (swidget->complete < 0) - return swidget->complete; + swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget); + if (swidget->spipe->complete < 0) + return swidget->spipe->complete; break; default: break; @@ -2261,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) for_each_pcm_streams(dir) { struct snd_pcm_substream *substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime) + if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) continue; if (spcm->stream[dir].list) { @@ -2313,8 +2316,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif /* Do not free widgets for static pipelines with FW older than SOF2.2 */ if (!verify && !swidget->dynamic_pipeline_widget && SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { + mutex_lock(&swidget->setup_mutex); swidget->use_count = 0; - swidget->complete = 0; + mutex_unlock(&swidget->setup_mutex); + if (swidget->spipe) + swidget->spipe->complete = 0; continue; } @@ -2423,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index, return 0; } +static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) +{ + if (link->no_pcm) + return 0; + + /* + * set default trigger order for all links. Exceptions to + * the rule will be handled in sof_pcm_dai_link_fixup() + * For playback, the sequence is the following: start FE, + * start BE, stop BE, stop FE; for Capture the sequence is + * inverted start BE, start FE, stop FE, stop BE + */ + link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE; + link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST; + + return 0; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2534,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, .parse_manifest = sof_ipc3_parse_manifest, + .link_setup = sof_ipc3_link_setup, }; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 1fef4dcc0936..3de64ea2dc9a 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -847,7 +847,7 @@ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); return; @@ -882,7 +882,7 @@ static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); return; @@ -1077,10 +1077,28 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev) return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); } +static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_pm_gate pm_gate; + struct sof_ipc_reply reply; + + memset(&pm_gate, 0, sizeof(pm_gate)); + + /* configure pm_gate ipc message */ + pm_gate.hdr.size = sizeof(pm_gate); + pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE; + pm_gate.flags = flags; + + /* send pm_gate ipc to dsp */ + return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate), + &reply, sizeof(reply)); +} + static const struct sof_ipc_pm_ops ipc3_pm_ops = { .ctx_save = sof_ipc3_ctx_save, .ctx_restore = sof_ipc3_ctx_restore, .set_core_state = sof_ipc3_set_core_state, + .set_pm_gate = sof_ipc3_set_pm_gate, }; const struct sof_ipc_ops ipc3_ops = { diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 0d5a578c3496..67bd2233fd9a 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -12,7 +12,8 @@ #include "ipc4-priv.h" #include "ipc4-topology.h" -static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) +static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, + bool set, bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; @@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool struct sof_ipc4_msg *msg = &cdata->msg; struct snd_sof_widget *swidget; bool widget_found = false; + int ret = 0; /* find widget associated with the control */ list_for_each_entry(swidget, &sdev->widget_list, list) { @@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool return -ENOENT; } + if (lock) + mutex_lock(&swidget->setup_mutex); + else + lockdep_assert_held(&swidget->setup_mutex); + /* - * Volatile controls should always be part of static pipelines and the widget use_count - * would always be > 0 in this case. For the others, just return the cached value if the - * widget is not set up. + * Volatile controls should always be part of static pipelines and the + * widget use_count would always be > 0 in this case. For the others, + * just return the cached value if the widget is not set up. */ if (!swidget->use_count) - return 0; + goto unlock; msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); - return iops->set_get_data(sdev, msg, msg->data_size, set); + ret = iops->set_get_data(sdev, msg, msg->data_size, set); + +unlock: + if (lock) + mutex_unlock(&swidget->setup_mutex); + + return ret; } static int sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol) + struct snd_sof_control *scontrol, bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_gain *gain = swidget->private; @@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge msg->data_ptr = &data; msg->data_size = sizeof(data); - ret = sof_ipc4_set_get_kcontrol_data(scontrol, true); + ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock); msg->data_ptr = NULL; msg->data_size = 0; if (ret < 0) { @@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); + ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); if (ret < 0) return false; @@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s list_for_each_entry(scontrol, &sdev->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); + ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); if (ret < 0) { dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", __func__, scontrol->comp_id, swidget->widget->name); diff --git a/sound/soc/sof/ipc4-fw-reg.h b/sound/soc/sof/ipc4-fw-reg.h new file mode 100644 index 000000000000..7226161e57e1 --- /dev/null +++ b/sound/soc/sof/ipc4-fw-reg.h @@ -0,0 +1,155 @@ +/* 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) 2022 Intel Corporation. All rights reserved. + */ + +#ifndef __IPC4_FW_REG_H__ +#define __IPC4_FW_REG_H__ + +#define SOF_IPC4_INVALID_STREAM_POSITION ULLONG_MAX + +/** + * struct sof_ipc4_pipeline_registers - Pipeline start and end information in fw + * @stream_start_offset: Stream start offset (LPIB) reported by mixin + * module allocated on pipeline attached to Host Output Gateway when + * first data is being mixed to mixout module. When data is not mixed + * (right after creation/after reset) value "(u64)-1" is reported + * @stream_end_offset: Stream end offset (LPIB) reported by mixin + * module allocated on pipeline attached to Host Output Gateway + * during transition from RUNNING to PAUSED. When data is not mixed + * (right after creation or after reset) value "(u64)-1" is reported. + * When first data is mixed then value "0"is reported. + */ +struct sof_ipc4_pipeline_registers { + u64 stream_start_offset; + u64 stream_end_offset; +} __packed __aligned(4); + +#define SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS 8 + +/** + * struct sof_ipc4_peak_volume_regs - Volume information in fw + * @peak_meter: Peak volume value in fw + * @current_volume: Current volume value in fw + * @target_volume: Target volume value in fw + */ +struct sof_ipc4_peak_volume_regs { + u32 peak_meter[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS]; + u32 current_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS]; + u32 target_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS]; +} __packed __aligned(4); + +/** + * struct sof_ipc4_llp_reading - Llp information in fw + * @llp_l: Lower part of 64-bit LLP + * @llp_u: Upper part of 64-bit LLP + * @wclk_l: Lower part of 64-bit Wallclock + * @wclk_u: Upper part of 64-bit Wallclock + */ +struct sof_ipc4_llp_reading { + u32 llp_l; + u32 llp_u; + u32 wclk_l; + u32 wclk_u; +} __packed __aligned(4); + +/** + * struct of sof_ipc4_llp_reading_extended - Extended llp info + * @llp_reading: Llp information in memory window + * @tpd_low: Total processed data (low part) + * @tpd_high: Total processed data (high part) + */ +struct sof_ipc4_llp_reading_extended { + struct sof_ipc4_llp_reading llp_reading; + u32 tpd_low; + u32 tpd_high; +} __packed __aligned(4); + +/** + * struct sof_ipc4_llp_reading_slot - Llp slot information in memory window + * @node_id: Dai gateway node id + * @reading: Llp information in memory window + */ +struct sof_ipc4_llp_reading_slot { + u32 node_id; + struct sof_ipc4_llp_reading reading; +} __packed __aligned(4); + +/* ROM information */ +#define SOF_IPC4_FW_FUSE_VALUE_MASK GENMASK(7, 0) +#define SOF_IPC4_FW_LOAD_METHOD_MASK BIT(8) +#define SOF_IPC4_FW_DOWNLINK_IPC_USE_DMA_MASK BIT(9) +#define SOF_IPC4_FW_LOAD_METHOD_REV_MASK GENMASK(11, 10) +#define SOF_IPC4_FW_REVISION_MIN_MASK GENMASK(15, 12) +#define SOF_IPC4_FW_REVISION_MAJ_MASK GENMASK(19, 16) +#define SOF_IPC4_FW_VERSION_MIN_MASK GENMASK(23, 20) +#define SOF_IPC4_FW_VERSION_MAJ_MASK GENMASK(27, 24) + +/* Number of dsp core supported in FW Regs. */ +#define SOF_IPC4_MAX_SUPPORTED_ADSP_CORES 8 + +/* Number of host pipeline registers slots in FW Regs. */ +#define SOF_IPC4_MAX_PIPELINE_REG_SLOTS 16 + +/* Number of PeakVol registers slots in FW Regs. */ +#define SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS 16 + +/* Number of GPDMA LLP Reading slots in FW Regs. */ +#define SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS 24 + +/* Number of Aggregated SNDW Reading slots in FW Regs. */ +#define SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS 15 + +/* Current ABI version of the Fw registers layout. */ +#define SOF_IPC4_FW_REGS_ABI_VER 1 + +/** + * struct sof_ipc4_fw_registers - FW Registers exposes additional + * DSP / FW state information to the driver + * @fw_status: Current ROM / FW status + * @lec: Last ROM / FW error code + * @fps: Current DSP clock status + * @lnec: Last Native Error Code(from external library) + * @ltr: Copy of LTRC HW register value(FW only) + * @rsvd0: Reserved0 + * @rom_info: ROM info + * @abi_ver: Version of the layout, set to the current FW_REGS_ABI_VER + * @slave_core_sts: Slave core states + * @rsvd2: Reserved2 + * @pipeline_regs: State of pipelines attached to host output gateways + * @peak_vol_regs: State of PeakVol instances indexed by the PeakVol's instance_id + * @llp_gpdma_reading_slots: LLP Readings for single link gateways + * @llp_sndw_reading_slots: SNDW aggregated link gateways + * @llp_evad_reading_slot: LLP Readings for EVAD gateway + */ +struct sof_ipc4_fw_registers { + u32 fw_status; + u32 lec; + u32 fps; + u32 lnec; + u32 ltr; + u32 rsvd0; + u32 rom_info; + u32 abi_ver; + u8 slave_core_sts[SOF_IPC4_MAX_SUPPORTED_ADSP_CORES]; + u32 rsvd2[6]; + + struct sof_ipc4_pipeline_registers + pipeline_regs[SOF_IPC4_MAX_PIPELINE_REG_SLOTS]; + + struct sof_ipc4_peak_volume_regs + peak_vol_regs[SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS]; + + struct sof_ipc4_llp_reading_slot + llp_gpdma_reading_slots[SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS]; + + struct sof_ipc4_llp_reading_slot + llp_sndw_reading_slots[SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS]; + + struct sof_ipc4_llp_reading_slot llp_evad_reading_slot; +} __packed __aligned(4); + +#endif diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 96941bebc1f1..68258767aace 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -10,8 +10,37 @@ #include <sound/sof/ipc4/header.h> #include "sof-audio.h" #include "sof-priv.h" +#include "ops.h" #include "ipc4-priv.h" #include "ipc4-topology.h" +#include "ipc4-fw-reg.h" + +static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, + struct ipc4_pipeline_set_state_data *trigger_list) +{ + struct sof_ipc4_msg msg = {{ 0 }}; + u32 primary, ipc_size; + + /* trigger a single pipeline */ + if (trigger_list->count == 1) + return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state); + + primary = state; + primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); + primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + msg.primary = primary; + + /* trigger multiple pipelines with a single IPC */ + msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI; + + /* ipc_size includes the count and the pipeline IDs for the number of pipelines */ + ipc_size = sizeof(u32) * (trigger_list->count + 1); + msg.data_size = ipc_size; + msg.data_ptr = trigger_list; + + return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0); +} int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state) { @@ -32,80 +61,238 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state) } EXPORT_SYMBOL(sof_ipc4_set_pipeline_state); +static void +sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, + struct snd_sof_pipeline *spipe, + struct ipc4_pipeline_set_state_data *trigger_list) +{ + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->skip_during_fe_trigger) + return; + + switch (state) { + case SOF_IPC4_PIPE_RUNNING: + /* + * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING + * for the first time + */ + if (spipe->started_count == spipe->paused_count) + trigger_list->pipeline_ids[trigger_list->count++] = + pipe_widget->instance_id; + break; + case SOF_IPC4_PIPE_RESET: + /* RESET if the pipeline is neither running nor paused */ + if (!spipe->started_count && !spipe->paused_count) + trigger_list->pipeline_ids[trigger_list->count++] = + pipe_widget->instance_id; + break; + case SOF_IPC4_PIPE_PAUSED: + /* Pause the pipeline only when its started_count is 1 more than paused_count */ + if (spipe->paused_count == (spipe->started_count - 1)) + trigger_list->pipeline_ids[trigger_list->count++] = + pipe_widget->instance_id; + break; + default: + break; + } +} + +static void +sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, + struct snd_sof_pipeline *spipe, + struct ipc4_pipeline_set_state_data *trigger_list) +{ + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + int i; + + if (pipeline->skip_during_fe_trigger) + return; + + /* set state for pipeline if it was just triggered */ + for (i = 0; i < trigger_list->count; i++) { + if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) { + pipeline->state = state; + break; + } + } + + switch (state) { + case SOF_IPC4_PIPE_PAUSED: + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* + * increment paused_count if the PAUSED is the final state during + * the PAUSE trigger + */ + spipe->paused_count++; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* + * decrement started_count if PAUSED is the final state during the + * STOP trigger + */ + spipe->started_count--; + break; + default: + break; + } + break; + case SOF_IPC4_PIPE_RUNNING: + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* decrement paused_count for RELEASE */ + spipe->paused_count--; + break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + /* increment started_count for START/RESUME */ + spipe->started_count++; + break; + default: + break; + } + break; + default: + break; + } +} + +/* + * The picture below represents the pipeline state machine wrt PCM actions corresponding to the + * triggers and ioctls + * +---------------+ + * | | + * | INIT | + * | | + * +-------+-------+ + * | + * | + * | START + * | + * | + * +----------------+ +------v-------+ +-------------+ + * | | START | | HW_FREE | | + * | RUNNING <-------------+ PAUSED +--------------> + RESET | + * | | PAUSE | | | | + * +------+---------+ RELEASE +---------+----+ +-------------+ + * | ^ + * | | + * | | + * | | + * | PAUSE | + * +---------------------------------+ + * STOP/SUSPEND + * + * Note that during system suspend, the suspend trigger is followed by a hw_free in + * sof_pcm_trigger(). So, the final state during suspend would be RESET. + * Also, since the SOF driver doesn't support full resume, streams would be restarted with the + * prepare ioctl before the START trigger. + */ + static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int state) + struct snd_pcm_substream *substream, int state, int cmd) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_sof_widget *pipeline_widget; - struct snd_soc_dapm_widget_list *list; - struct snd_soc_dapm_widget *widget; - struct sof_ipc4_pipeline *pipeline; - struct snd_sof_widget *swidget; + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct ipc4_pipeline_set_state_data *trigger_list; + struct snd_sof_pipeline *spipe; struct snd_sof_pcm *spcm; - int ret = 0; - int num_widgets; + int ret; + int i; + + dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state); spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; - list = spcm->stream[substream->stream].list; + pipeline_list = &spcm->stream[substream->stream].pipeline_list; - for_each_dapm_widgets(list, num_widgets, widget) { - swidget = widget->dobj.private; + /* nothing to trigger if the list is empty */ + if (!pipeline_list->pipelines || !pipeline_list->count) + return 0; - if (!swidget) - continue; + /* allocate memory for the pipeline data */ + trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), + GFP_KERNEL); + if (!trigger_list) + return -ENOMEM; - /* - * set pipeline state for both FE and BE pipelines for RUNNING state. - * For PAUSE/RESET, set the pipeline state only for the FE pipeline. - */ - switch (state) { - case SOF_IPC4_PIPE_PAUSED: - case SOF_IPC4_PIPE_RESET: - if (!WIDGET_IS_AIF(swidget->id)) - continue; - break; - default: - break; - } + mutex_lock(&ipc4_data->pipeline_state_mutex); - /* find pipeline widget for the pipeline that this widget belongs to */ - pipeline_widget = swidget->pipe_widget; - pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private; + /* + * IPC4 requires pipelines to be triggered in order starting at the sink and + * walking all the way to the source. So traverse the pipeline_list in the order + * sink->source when starting PCM's and in the reverse order to pause/stop PCM's. + * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork + * in the pipeline, the order of triggering between the left/right paths will be + * indeterministic. But the sink->source trigger order sink->source would still be + * guaranteed for each fork independently. + */ + if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET) + for (i = pipeline_list->count - 1; i >= 0; i--) { + spipe = pipeline_list->pipelines[i]; + sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list); + } + else + for (i = 0; i < pipeline_list->count; i++) { + spipe = pipeline_list->pipelines[i]; + sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list); + } - if (pipeline->state == state) - continue; + /* return if all pipelines are in the requested state already */ + if (!trigger_list->count) { + ret = 0; + goto free; + } - /* first set the pipeline to PAUSED state */ - if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { - ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, - SOF_IPC4_PIPE_PAUSED); - if (ret < 0) { - dev_err(sdev->dev, "failed to pause pipeline %d\n", - swidget->pipeline_id); - return ret; - } - } + /* no need to pause before reset or before pause release */ + if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) + goto skip_pause_transition; - pipeline->state = SOF_IPC4_PIPE_PAUSED; + /* + * set paused state for pipelines if the final state is PAUSED or when the pipeline + * is set to RUNNING for the first time after the PCM is started. + */ + ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); + if (ret < 0) { + dev_err(sdev->dev, "failed to pause all pipelines\n"); + goto free; + } - if (pipeline->state == state) - continue; + /* update PAUSED state for all pipelines just triggered */ + for (i = 0; i < pipeline_list->count ; i++) { + spipe = pipeline_list->pipelines[i]; + sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe, + trigger_list); + } - /* then set the final state */ - ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state); - if (ret < 0) { - dev_err(sdev->dev, "failed to set state %d for pipeline %d\n", - state, swidget->pipeline_id); - break; - } + /* return if this is the final state */ + if (state == SOF_IPC4_PIPE_PAUSED) + goto free; +skip_pause_transition: + /* else set the RUNNING/RESET state in the DSP */ + ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); + if (ret < 0) { + dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state); + goto free; + } - pipeline->state = state; + /* update RUNNING/RESET state for all pipelines that were just triggered */ + for (i = 0; i < pipeline_list->count; i++) { + spipe = pipeline_list->pipelines[i]; + sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list); } +free: + mutex_unlock(&ipc4_data->pipeline_state_mutex); + kfree(trigger_list); return ret; } @@ -134,13 +321,14 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component, } /* set the pipeline state */ - return sof_ipc4_trigger_pipelines(component, substream, state); + return sof_ipc4_trigger_pipelines(component, substream, state, cmd); } static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET); + /* command is not relevant with RESET, so just pass 0 */ + return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0); } static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, @@ -183,7 +371,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct sof_ipc4_copier *ipc4_copier; - struct snd_soc_dpcm *dpcm; if (!dai) { dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, @@ -205,17 +392,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency; rate->max = rate->min; - /* - * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required - * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI - * pipeline gets triggered and the pipeline widgets are freed. - */ - for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) { - struct snd_soc_pcm_runtime *fe = dpcm->fe; - - fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; - } - switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP: ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); @@ -227,8 +403,273 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) +{ + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + int stream; + + for_each_pcm_streams(stream) { + pipeline_list = &spcm->stream[stream].pipeline_list; + kfree(pipeline_list->pipelines); + pipeline_list->pipelines = NULL; + kfree(spcm->stream[stream].private); + spcm->stream[stream].private = NULL; + } +} + +static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) +{ + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_timestamp_info *stream_info; + bool support_info = true; + u32 abi_version; + u32 abi_offset; + int stream; + + abi_offset = offsetof(struct sof_ipc4_fw_registers, abi_ver); + sof_mailbox_read(sdev, sdev->fw_info_box.offset + abi_offset, &abi_version, + sizeof(abi_version)); + + if (abi_version < SOF_IPC4_FW_REGS_ABI_VER) + support_info = false; + + for_each_pcm_streams(stream) { + pipeline_list = &spcm->stream[stream].pipeline_list; + + /* allocate memory for max number of pipeline IDs */ + pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines, + sizeof(struct snd_sof_widget *), GFP_KERNEL); + if (!pipeline_list->pipelines) { + sof_ipc4_pcm_free(sdev, spcm); + return -ENOMEM; + } + + if (!support_info) + continue; + + stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL); + if (!stream_info) { + sof_ipc4_pcm_free(sdev, spcm); + return -ENOMEM; + } + + spcm->stream[stream].private = stream_info; + } + + return 0; +} + +static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm) +{ + struct sof_ipc4_copier *host_copier = NULL; + struct sof_ipc4_copier *dai_copier = NULL; + struct sof_ipc4_llp_reading_slot llp_slot; + struct sof_ipc4_timestamp_info *info; + struct snd_soc_dapm_widget *widget; + struct snd_sof_dai *dai; + int i; + + /* find host & dai to locate info in memory window */ + for_each_dapm_widgets(spcm->list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + if (WIDGET_IS_AIF(swidget->widget->id)) { + host_copier = swidget->private; + } else if (WIDGET_IS_DAI(swidget->widget->id)) { + dai = swidget->private; + dai_copier = dai->private; + } + } + + /* both host and dai copier must be valid for time_info */ + if (!host_copier || !dai_copier) { + dev_err(sdev->dev, "host or dai copier are not found\n"); + return; + } + + info = spcm->private; + info->host_copier = host_copier; + info->dai_copier = dai_copier; + info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + + sdev->fw_info_box.offset; + + /* find llp slot used by current dai */ + for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) { + sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) + break; + + info->llp_offset += sizeof(llp_slot); + } + + if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS) + return; + + /* if no llp gpdma slot is used, check aggregated sdw slot */ + info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) + + sdev->fw_info_box.offset; + for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) { + sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) + break; + + info->llp_offset += sizeof(llp_slot); + } + + if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS) + return; + + /* check EVAD slot */ + info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) + + sdev->fw_info_box.offset; + sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) { + dev_info(sdev->dev, "no llp found, fall back to default HDA path"); + info->llp_offset = 0; + } +} + +static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc4_timestamp_info *time_info; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + time_info = spcm->stream[substream->stream].private; + /* delay calculation is not supported by current fw_reg ABI */ + if (!time_info) + return 0; + + time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; + time_info->llp_offset = 0; + + sof_ipc4_build_time_info(sdev, &spcm->stream[substream->stream]); + + return 0; +} + +static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *stream, + struct sof_ipc4_timestamp_info *time_info) +{ + struct sof_ipc4_copier *host_copier = time_info->host_copier; + struct sof_ipc4_copier *dai_copier = time_info->dai_copier; + struct sof_ipc4_pipeline_registers ppl_reg; + u64 stream_start_position; + u32 dai_sample_size; + u32 ch, node_index; + u32 offset; + + if (!host_copier || !dai_copier) + return -EINVAL; + + if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) + return -EINVAL; + + node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id); + offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg); + sof_mailbox_read(sdev, sdev->fw_info_box.offset + offset, &ppl_reg, sizeof(ppl_reg)); + if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) + return -EINVAL; + + stream_start_position = ppl_reg.stream_start_offset; + ch = dai_copier->data.out_format.fmt_cfg; + ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch); + dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch; + /* convert offset to sample count */ + do_div(stream_start_position, dai_sample_size); + time_info->stream_start_offset = stream_start_position; + + return 0; +} + +static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc4_timestamp_info *time_info; + struct sof_ipc4_llp_reading_slot llp; + snd_pcm_uframes_t head_ptr, tail_ptr; + struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm *spcm; + u64 tmp_ptr; + int ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return 0; + + stream = &spcm->stream[substream->stream]; + time_info = stream->private; + if (!time_info) + return 0; + + /* + * stream_start_offset is updated to memory window by FW based on + * pipeline statistics and it may be invalid if host query happens before + * the statistics is complete. And it will not change after the first initiailization. + */ + if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { + ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info); + if (ret < 0) + return 0; + } + + /* + * HDaudio links don't support the LLP counter reported by firmware + * the link position is read directly from hardware registers. + */ + if (!time_info->llp_offset) { + tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream); + if (!tmp_ptr) + return 0; + } else { + sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp)); + tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; + } + + /* In two cases dai dma position is not accurate + * (1) dai pipeline is started before host pipeline + * (2) multiple streams mixed into one. Each stream has the same dai dma position + * + * Firmware calculates correct stream_start_offset for all cases including above two. + * Driver subtracts stream_start_offset from dai dma position to get accurate one + */ + tmp_ptr -= time_info->stream_start_offset; + + /* Calculate the delay taking into account that both pointer can wrap */ + div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + head_ptr = substream->runtime->status->hw_ptr; + tail_ptr = tmp_ptr; + } else { + head_ptr = tmp_ptr; + tail_ptr = substream->runtime->status->hw_ptr; + } + + if (head_ptr < tail_ptr) + return substream->runtime->boundary - tail_ptr + head_ptr; + + return head_ptr - tail_ptr; +} + const struct sof_ipc_pcm_ops ipc4_pcm_ops = { + .hw_params = sof_ipc4_pcm_hw_params, .trigger = sof_ipc4_pcm_trigger, .hw_free = sof_ipc4_pcm_hw_free, .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup, + .pcm_setup = sof_ipc4_pcm_setup, + .pcm_free = sof_ipc4_pcm_free, + .delay = sof_ipc4_pcm_delay }; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index fc9efdce67e0..f461b8c70df3 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -14,6 +14,7 @@ #include "sof-priv.h" /* The DSP window indices are fixed */ +#define SOF_IPC4_INBOX_WINDOW_IDX 0 #define SOF_IPC4_OUTBOX_WINDOW_IDX 1 #define SOF_IPC4_DEBUG_WINDOW_IDX 2 @@ -70,6 +71,7 @@ struct sof_ipc4_fw_library { * base firmware * * @load_library: Callback function for platform dependent library loading + * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion */ struct sof_ipc4_fw_data { u32 manifest_fw_hdr_offset; @@ -82,6 +84,21 @@ struct sof_ipc4_fw_data { int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); + struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ +}; + +/** + * struct sof_ipc4_timestamp_info - IPC4 timestamp info + * @host_copier: the host copier of the pcm stream + * @dai_copier: the dai copier of the pcm stream + * @stream_start_offset: reported by fw in memory window + * @llp_offset: llp offset in memory window + */ +struct sof_ipc4_timestamp_info { + struct sof_ipc4_copier *host_copier; + struct sof_ipc4_copier *dai_copier; + u64 stream_start_offset; + u32 llp_offset; }; extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 59f4d42f9011..3e27c7a48ebd 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -354,6 +354,13 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) goto free_available_fmt; } + /* + * This callback is used by host copier and module-to-module copier, + * and only host copier needs to set gtw_cfg. + */ + if (!WIDGET_IS_AIF(swidget->id)) + goto skip_gtw_cfg; + ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size, SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(u32), @@ -380,7 +387,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) } dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type); - ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); +skip_gtw_cfg: ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); if (!ipc4_copier->gtw_attr) { ret = -ENOMEM; @@ -391,6 +398,21 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) ipc4_copier->data.gtw_cfg.config_length = sizeof(struct sof_ipc4_gtw_attributes) >> 2; + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); + break; + case snd_soc_dapm_buffer: + ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; + ipc4_copier->ipc_config_size = 0; + break; + default: + dev_err(scomp->dev, "invalid widget type %d\n", swidget->id); + ret = -EINVAL; + goto free_gtw_attr; + } + /* set up module info and message header */ ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg); if (ret) @@ -833,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg total = SOF_IPC4_FW_PAGE(task_mem + queue_mem); - pipe_widget = swidget->pipe_widget; + pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage += total; } @@ -947,11 +969,11 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) struct sof_ipc4_pipeline *pipeline; /* reset pipeline memory usage */ - pipe_widget = swidget->pipe_widget; + pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage = 0; - if (WIDGET_IS_AIF(swidget->id)) { + if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { ipc4_copier = swidget->private; } else if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private; @@ -1114,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; - pipe_widget = swidget->pipe_widget; + pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; ipc4_copier = (struct sof_ipc4_copier *)swidget->private; gtw_attr = ipc4_copier->gtw_attr; @@ -1177,6 +1199,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, break; } + case snd_soc_dapm_buffer: + { + ipc4_copier = (struct sof_ipc4_copier *)swidget->private; + copier_data = &ipc4_copier->data; + available_fmt = &ipc4_copier->available_fmt; + + /* + * base_config->audio_fmt represent the input audio formats. Use + * the input format as the reference to match pcm params + */ + available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt; + ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg); + ref_params = pipeline_params; + + break; + } default: dev_err(sdev->dev, "unsupported type %d for copier %s", swidget->id, swidget->widget->name); @@ -1203,8 +1241,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct sof_ipc4_copier_data *alh_data; struct sof_ipc4_copier *alh_copier; struct snd_sof_widget *w; + u32 ch_count = 0; u32 ch_mask = 0; u32 ch_map; + u32 step; + u32 mask; int i; blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; @@ -1214,11 +1255,15 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* Get channel_mask from ch_map */ ch_map = copier_data->base_config.audio_fmt.ch_map; for (i = 0; ch_map; i++) { - if ((ch_map & 0xf) != 0xf) + if ((ch_map & 0xf) != 0xf) { ch_mask |= BIT(i); + ch_count++; + } ch_map >>= 4; } + step = ch_count / blob->alh_cfg.count; + mask = GENMASK(step - 1, 0); /* * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[] * for all widgets with the same stream name @@ -1233,7 +1278,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id; - blob->alh_cfg.mapping[i].channel_mask = ch_mask; + /* + * Set the same channel mask for playback as the audio data is + * duplicated for all speakers. For capture, split the channels + * among the aggregated DAIs. For example, with 4 channels on 2 + * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the + * two DAI's. + * The channel masks used depend on the cpu_dais used in the + * dailink at the machine driver level, which actually comes from + * the tables in soc_acpi files depending on the _ADR and devID + * registers for each codec. + */ + if (w->id == snd_soc_dapm_dai_in) + blob->alh_cfg.mapping[i].channel_mask = ch_mask; + else + blob->alh_cfg.mapping[i].channel_mask = mask << (step * i); + i++; } if (blob->alh_cfg.count > 1) { @@ -1435,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pipeline *pipeline; struct sof_ipc4_msg *msg; @@ -1465,6 +1525,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget break; case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: + case snd_soc_dapm_buffer: { struct sof_ipc4_copier *ipc4_copier = swidget->private; @@ -1564,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; int ret = 0; + mutex_lock(&ipc4_data->pipeline_state_mutex); + /* freeing a pipeline frees all the widgets associated with it */ if (swidget->id == snd_soc_dapm_scheduler) { struct sof_ipc4_pipeline *pipeline = swidget->private; @@ -1591,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ida_free(&fw_module->m_ida, swidget->instance_id); } + mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } @@ -1676,6 +1742,55 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id, ida_free(queue_ida, queue_id); } +static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, + struct snd_sof_widget *src_widget, + struct snd_sof_widget *sink_widget, + int sink_id) +{ + struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private; + struct sof_ipc4_base_module_cfg *src_config; + struct sof_ipc4_copier_config_set_sink_format format; + struct sof_ipc4_fw_module *fw_module; + struct sof_ipc4_msg msg = {{ 0 }}; + u32 header, extension; + + dev_dbg(sdev->dev, "%s set copier sink %d format\n", + src_widget->widget->name, sink_id); + + if (WIDGET_IS_DAI(src_widget->id)) { + struct snd_sof_dai *dai = src_widget->private; + + src_config = dai->private; + } else { + src_config = src_widget->private; + } + + fw_module = src_widget->module_info; + + format.sink_id = sink_id; + memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt)); + memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt)); + msg.data_size = sizeof(format); + msg.data_ptr = &format; + + header = fw_module->man4_module_entry.id; + header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); + header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); + header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + + extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size); + extension |= + SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT); + extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1); + extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); + + msg.primary = header; + msg.extension = extension; + + return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0); +} + static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { struct snd_sof_widget *src_widget = sroute->src_widget; @@ -1704,6 +1819,17 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * return sroute->dst_queue_id; } + /* Pin 0 format is already set during copier module init */ + if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) { + ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, + sroute->src_queue_id); + if (ret < 0) { + dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n", + src_widget->widget->name, sroute->src_queue_id); + goto out; + } + } + dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -1724,16 +1850,18 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); if (ret < 0) { - dev_err(sdev->dev, "%s: failed to bind modules %s -> %s\n", - __func__, src_widget->widget->name, sink_widget->widget->name); - - sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, - SOF_PIN_TYPE_SOURCE); - sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, - SOF_PIN_TYPE_SINK); + dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n", + src_widget->widget->name, sroute->src_queue_id, + sink_widget->widget->name, sroute->dst_queue_id); + goto out; } return ret; + +out: + sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); + sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); + return ret; } static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) @@ -1744,12 +1872,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; struct sof_ipc4_msg msg = {{ 0 }}; u32 header, extension; - int ret; + int ret = 0; dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); + /* + * routes belonging to the same pipeline will be disconnected by the FW when the pipeline + * is freed. So avoid sending this IPC which will be ignored by the FW anyway. + */ + if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget) + goto out; + header = src_fw_module->man4_module_entry.id; header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND); @@ -1766,9 +1901,10 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); if (ret < 0) - dev_err(sdev->dev, "failed to unbind modules %s -> %s\n", - src_widget->widget->name, sink_widget->widget->name); - + dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n", + src_widget->widget->name, sroute->src_queue_id, + sink_widget->widget->name, sroute->dst_queue_id); +out: sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); @@ -1778,7 +1914,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data) { - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_gtw_attributes *gtw_attr; @@ -1801,6 +1937,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * case SOF_DAI_INTEL_HDA: gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; + pipeline->skip_during_fe_trigger = true; fallthrough; case SOF_DAI_INTEL_ALH: copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; @@ -1957,7 +2094,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif for_each_pcm_streams(dir) { struct snd_pcm_substream *substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime) + if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) continue; if (spcm->stream[dir].list) { @@ -1970,7 +2107,25 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif return 0; } -static enum sof_tokens host_token_list[] = { +static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) +{ + if (link->no_pcm) + return 0; + + /* + * set default trigger order for all links. Exceptions to + * the rule will be handled in sof_pcm_dai_link_fixup() + * For playback, the sequence is the following: start BE, + * start FE, stop FE, stop BE; for Capture the sequence is + * inverted start FE, start BE, stop BE, stop FE + */ + link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST; + link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; + + return 0; +} + +static enum sof_tokens common_copier_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, @@ -2026,12 +2181,12 @@ static enum sof_tokens src_token_list[] = { static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - host_token_list, ARRAY_SIZE(host_token_list), NULL, - sof_ipc4_prepare_copier_module, + common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - host_token_list, ARRAY_SIZE(host_token_list), NULL, - sof_ipc4_prepare_copier_module, + common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, dai_token_list, ARRAY_SIZE(dai_token_list), NULL, @@ -2041,6 +2196,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY dai_token_list, ARRAY_SIZE(dai_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, + [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, + common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + NULL, sof_ipc4_prepare_copier_module, + sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, sof_ipc4_widget_free_comp_pipeline, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL, @@ -2072,4 +2231,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .parse_manifest = sof_ipc4_parse_manifest, .dai_get_clk = sof_ipc4_dai_get_clk, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, + .link_setup = sof_ipc4_link_setup, }; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 0aa87a8add5d..72529179ac22 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -53,6 +53,8 @@ #define ALH_MAX_NUMBER_OF_GTW 16 +#define SOF_IPC4_INVALID_NODE_ID 0xffffffff + /* * The base of multi-gateways. Multi-gateways addressing starts from * ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources @@ -64,6 +66,52 @@ /* A magic number from FW */ #define ALH_MULTI_GTW_COUNT 8 +enum sof_ipc4_copier_module_config_params { +/* + * Use LARGE_CONFIG_SET to initialize timestamp event. Ipc mailbox must + * contain properly built CopierConfigTimestampInitData struct. + */ + SOF_IPC4_COPIER_MODULE_CFG_PARAM_TIMESTAMP_INIT = 1, +/* + * Use LARGE_CONFIG_SET to initialize copier sink. Ipc mailbox must contain + * properly built CopierConfigSetSinkFormat struct. + */ + SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT, +/* + * Use LARGE_CONFIG_SET to initialize and enable on Copier data segment + * event. Ipc mailbox must contain properly built DataSegmentEnabled struct. + */ + SOF_IPC4_COPIER_MODULE_CFG_PARAM_DATA_SEGMENT_ENABLED, +/* + * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non + * HD-A gateways. + */ + SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING, +/* + * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non + * HD-A gateways and corresponding total processed data + */ + SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING_EXTENDED, +/* + * Use LARGE_CONFIG_SET to setup attenuation on output pins. Data is just uint32_t. + * note Config is only allowed when output pin is set up for 32bit and source + * is connected to Gateway + */ + SOF_IPC4_COPIER_MODULE_CFG_ATTENUATION, +}; + +struct sof_ipc4_copier_config_set_sink_format { +/* Id of sink */ + u32 sink_id; +/* + * Input format used by the source + * attention must be the same as present if already initialized. + */ + struct sof_ipc4_audio_format source_fmt; +/* Output format used by the sink */ + struct sof_ipc4_audio_format sink_fmt; +} __packed __aligned(4); + /** * struct sof_ipc4_pipeline - pipeline config data * @priority: Priority of this pipeline @@ -71,6 +119,7 @@ * @mem_usage: Memory usage * @state: Pipeline state * @msg: message structure for pipeline + * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger */ struct sof_ipc4_pipeline { uint32_t priority; @@ -78,9 +127,20 @@ struct sof_ipc4_pipeline { uint32_t mem_usage; int state; struct sof_ipc4_msg msg; + bool skip_during_fe_trigger; }; /** + * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data + * @count: Number of pipelines to be triggered + * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered + */ +struct ipc4_pipeline_set_state_data { + u32 count; + DECLARE_FLEX_ARRAY(u32, pipeline_ids); +} __packed; + +/** * struct sof_ipc4_available_audio_format - Available audio formats * @base_config: Available base config * @out_audio_fmt: Available output audio format diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 74cd7e956019..8ede4b952997 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -13,6 +13,7 @@ #include <sound/sof/ipc4/header.h> #include "sof-priv.h" #include "sof-audio.h" +#include "ipc4-fw-reg.h" #include "ipc4-priv.h" #include "ops.h" @@ -369,6 +370,17 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ if (!msg_data) return -EINVAL; + if (!no_pm) { + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + + /* ensure the DSP is in D0i0 before sending a new IPC */ + ret = snd_sof_dsp_set_power_state(sdev, &target_state); + if (ret < 0) + return ret; + } + /* Serialise IPC TX */ mutex_lock(&ipc->tx_mutex); @@ -542,6 +554,8 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX); outbox_size = SOF_IPC4_MSG_MAX_SIZE; + sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX); + sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers); sdev->dsp_box.offset = inbox_offset; sdev->dsp_box.size = inbox_size; sdev->host_box.offset = outbox_offset; @@ -653,15 +667,30 @@ static int sof_ipc4_ctx_save(struct snd_sof_dev *sdev) return sof_ipc4_set_core_state(sdev, SOF_DSP_PRIMARY_CORE, false); } +static int sof_ipc4_set_pm_gate(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc4_msg msg = {{0}}; + + msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_SET_D0IX); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.extension = flags; + + return sof_ipc4_tx_msg(sdev, &msg, 0, NULL, 0, true); +} + static const struct sof_ipc_pm_ops ipc4_pm_ops = { .ctx_save = sof_ipc4_ctx_save, .set_core_state = sof_ipc4_set_core_state, + .set_pm_gate = sof_ipc4_set_pm_gate, }; static int sof_ipc4_init(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + mutex_init(&ipc4_data->pipeline_state_mutex); + xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); return 0; diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 79da25725987..597cb4476acb 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -494,7 +494,7 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev, } stream = &spcm->stream[substream->stream]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); return 0; @@ -588,6 +588,9 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = { .drv = mt8186_dai, .num_drv = ARRAY_SIZE(mt8186_dai), + /* Debug information */ + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + /* PM */ .suspend = mt8186_dsp_suspend, .resume = mt8186_dsp_resume, @@ -625,8 +628,25 @@ static const struct sof_dev_desc sof_of_mt8186_desc = { .ops = &sof_mt8186_ops, }; +static const struct sof_dev_desc sof_of_mt8188_desc = { + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "mediatek/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "mediatek/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-mt8188.ri", + }, + .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg", + .ops = &sof_mt8186_ops, +}; + static const struct of_device_id sof_of_mt8186_ids[] = { { .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc}, + { .compatible = "mediatek,mt8188-dsp", .data = &sof_of_mt8188_desc}, { } }; MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h index 98b2965e5ba6..5b521c60b4e3 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.h +++ b/sound/soc/sof/mediatek/mt8186/mt8186.h @@ -52,7 +52,15 @@ struct snd_sof_dev; #define ADSP_PRID 0x0 #define ADSP_ALTVEC_C0 0x04 #define ADSP_ALTVECSEL 0x0C -#define ADSP_ALTVECSEL_C0 BIT(1) +#define MT8188_ADSP_ALTVECSEL_C0 BIT(0) +#define MT8186_ADSP_ALTVECSEL_C0 BIT(1) + +/* + * On MT8188, BIT(1) is not evaluated and on MT8186 BIT(0) is not evaluated: + * We can simplify the driver by safely setting both bits regardless of the SoC. + */ +#define ADSP_ALTVECSEL_C0 (MT8188_ADSP_ALTVECSEL_C0 | \ + MT8186_ADSP_ALTVECSEL_C0) /* dsp bus */ #define ADSP_SRAM_POOL_CON 0x190 diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 3c81e84fcecf..42bae574c87a 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -215,11 +215,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) adsp->pa_sram = (phys_addr_t)mmio->start; adsp->sramsize = resource_size(mmio); - if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) { - dev_err(dev, "adsp SRAM(%#x) is not enough for share\n", - adsp->sramsize); - return -EINVAL; - } dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize); @@ -525,7 +520,7 @@ static snd_pcm_uframes_t mt8195_pcm_pointer(struct snd_sof_dev *sdev, } stream = &spcm->stream[substream->stream]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); return 0; @@ -642,6 +637,7 @@ static struct snd_sof_dsp_ops sof_mt8195_ops = { /* Debug information */ .dbg_dump = mt8195_adsp_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* DAI drivers */ .drv = mt8195_dai, diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h index 7ffd523f936c..b4229170049f 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.h +++ b/sound/soc/sof/mediatek/mt8195/mt8195.h @@ -139,8 +139,6 @@ struct snd_sof_dev; #define DSP_MBOX1_BAR 6 #define DSP_MBOX2_BAR 7 -#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0 - #define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/ #define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/ diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 3537805070ad..b13bfdeb2b70 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -103,14 +103,8 @@ static int sof_nocodec_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, card); } -static int sof_nocodec_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver sof_nocodec_audio = { .probe = sof_nocodec_probe, - .remove = sof_nocodec_remove, .driver = { .name = "sof-nocodec", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 3b3f3cf7af38..9ab7b9be765b 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -472,19 +472,19 @@ static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev) /* host DSP message data */ static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz) { - return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz); + return sof_ops(sdev)->ipc_msg_data(sdev, sps, p, sz); } /* host side configuration of the stream's data offset in stream mailbox area */ static inline int snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset) { if (sof_ops(sdev) && sof_ops(sdev)->set_stream_data_offset) - return sof_ops(sdev)->set_stream_data_offset(sdev, substream, + return sof_ops(sdev)->set_stream_data_offset(sdev, sps, posn_offset); return 0; @@ -511,6 +511,16 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, return 0; } +static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position) + return sof_ops(sdev)->get_stream_position(sdev, component, substream); + + return 0; +} + /* machine driver */ static inline int snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 14571b821eca..445acb5c3a21 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -125,8 +125,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_platform_stream_params platform_params = { 0 }; - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_pcm *spcm; int ret; @@ -143,7 +143,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { ret = pcm_ops->hw_free(component, substream); if (ret < 0) return ret; @@ -177,7 +177,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - if (pcm_ops->hw_params) { + if (pcm_ops && pcm_ops->hw_params) { ret = pcm_ops->hw_params(component, substream, params, &platform_params); if (ret < 0) return ret; @@ -196,7 +196,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; int ret, err = 0; @@ -212,7 +212,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm->pcm.pcm_id, substream->stream); /* free PCM in the DSP */ - if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { ret = pcm_ops->hw_free(component, substream); if (ret < 0) err = ret; @@ -279,10 +279,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; bool reset_hw_params = false; - bool free_widget_list = false; bool ipc_first = false; int ret = 0; @@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } - free_widget_list = true; fallthrough; case SNDRV_PCM_TRIGGER_STOP: ipc_first = true; @@ -344,7 +342,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (!ipc_first) snd_sof_pcm_platform_trigger(sdev, substream, cmd); - if (pcm_ops->trigger) + if (pcm_ops && pcm_ops->trigger) ret = pcm_ops->trigger(component, substream, cmd); /* need to STOP DMA even if trigger IPC failed */ @@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, - free_widget_list); + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); return ret; } @@ -569,7 +566,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); /* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -590,7 +587,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa return 0; } - if (pcm_ops->dai_link_fixup) + if (pcm_ops && pcm_ops->dai_link_fixup) return pcm_ops->dai_link_fixup(rtd, params); return 0; @@ -649,6 +646,18 @@ static int sof_pcm_ack(struct snd_soc_component *component, return snd_sof_pcm_platform_ack(sdev, substream); } +static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + + if (pcm_ops && pcm_ops->delay) + return pcm_ops->delay(component, substream); + + return 0; +} + void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; @@ -673,6 +682,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->trigger = sof_pcm_trigger; pd->pointer = sof_pcm_pointer; pd->ack = sof_pcm_ack; + pd->delay = sof_pcm_delay; #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) pd->compress_ops = &sof_compressed_ops; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8722bbd7fd3d..8d3383085d12 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -73,8 +73,8 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); u32 old_state = sdev->dsp_power_state.state; int ret; @@ -155,7 +155,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) } /* restore pipelines */ - if (tplg_ops->set_up_all_pipelines) { + if (tplg_ops && tplg_ops->set_up_all_pipelines) { ret = tplg_ops->set_up_all_pipelines(sdev, false); if (ret < 0) { dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); @@ -179,8 +179,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); pm_message_t pm_state; u32 target_state = snd_sof_dsp_power_target(sdev); int ret; @@ -276,7 +276,7 @@ suspend: int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) { - const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); /* Notify DSP of upcoming power down */ if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 865c367eb2f2..760621bfc802 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -16,21 +16,23 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) { - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_route *sroute; list_for_each_entry(sroute, &sdev->route_list, list) if (sroute->src_widget == widget || sroute->sink_widget == widget) { - if (sroute->setup && tplg_ops->route_free) + if (sroute->setup && tplg_ops && tplg_ops->route_free) tplg_ops->route_free(sdev, sroute); sroute->setup = false; } } -int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) { - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_widget *pipe_widget; int err = 0; int ret; @@ -43,11 +45,13 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; + pipe_widget = swidget->spipe->pipe_widget; + /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); /* continue to disable core even if IPC fails */ - if (tplg_ops->widget_free) + if (tplg_ops && tplg_ops->widget_free) err = tplg_ops->widget_free(sdev, swidget); /* @@ -67,22 +71,38 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) * skip for static pipelines */ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free(sdev, swidget->pipe_widget); + ret = sof_widget_free_unlocked(sdev, pipe_widget); if (ret < 0 && !err) err = ret; - swidget->pipe_widget->complete = 0; } + /* clear pipeline complete */ + if (swidget->id == snd_soc_dapm_scheduler) + swidget->spipe->complete = 0; + if (!err) dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); return err; } + +int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + int ret; + + mutex_lock(&swidget->setup_mutex); + ret = sof_widget_free_unlocked(sdev, swidget); + mutex_unlock(&swidget->setup_mutex); + + return ret; +} EXPORT_SYMBOL(sof_widget_free); -int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) { - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + bool use_count_decremented = false; int ret; /* skip if there is no private data */ @@ -103,14 +123,13 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines. */ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - if (!swidget->pipe_widget) { - dev_err(sdev->dev, "No scheduler widget set for %s\n", - swidget->widget->name); + if (!swidget->spipe || !swidget->spipe->pipe_widget) { + dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name); ret = -EINVAL; goto use_count_dec; } - ret = sof_widget_setup(sdev, swidget->pipe_widget); + ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget); if (ret < 0) goto use_count_dec; } @@ -124,7 +143,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } /* setup widget in the DSP */ - if (tplg_ops->widget_setup) { + if (tplg_ops && tplg_ops->widget_setup) { ret = tplg_ops->widget_setup(sdev, swidget); if (ret < 0) goto core_put; @@ -134,7 +153,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (WIDGET_IS_DAI(swidget->id)) { unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE; - if (tplg_ops->dai_config) { + if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, NULL); if (ret < 0) goto widget_free; @@ -142,7 +161,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } /* restore kcontrols for widget */ - if (tplg_ops->control->widget_kcontrol_setup) { + if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) { ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget); if (ret < 0) goto widget_free; @@ -154,14 +173,28 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) widget_free: /* widget use_count and core ref_count will both be decremented by sof_widget_free() */ - sof_widget_free(sdev, swidget); + sof_widget_free_unlocked(sdev, swidget); + use_count_decremented = true; core_put: snd_sof_dsp_core_put(sdev, swidget->core); pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) - sof_widget_free(sdev, swidget->pipe_widget); + sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); use_count_dec: - swidget->use_count--; + if (!use_count_decremented) + swidget->use_count--; + + return ret; +} + +int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + int ret; + + mutex_lock(&swidget->setup_mutex); + ret = sof_widget_setup_unlocked(sdev, swidget); + mutex_unlock(&swidget->setup_mutex); + return ret; } EXPORT_SYMBOL(sof_widget_setup); @@ -169,12 +202,11 @@ EXPORT_SYMBOL(sof_widget_setup); int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink) { - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *src_widget = wsource->dobj.private; struct snd_sof_widget *sink_widget = wsink->dobj.private; struct snd_sof_route *sroute; bool route_found = false; - int ret; /* ignore routes involving virtual widgets in topology */ switch (src_widget->id) { @@ -212,9 +244,12 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc if (sroute->setup) return 0; - ret = ipc_tplg_ops->route_setup(sdev, sroute); - if (ret < 0) - return ret; + if (tplg_ops && tplg_ops->route_setup) { + int ret = tplg_ops->route_setup(sdev, sroute); + + if (ret < 0) + return ret; + } sroute->setup = true; return 0; @@ -239,24 +274,32 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, if (!widget->dobj.private) continue; - snd_soc_dapm_widget_for_each_sink_path(widget, p) + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!widget_in_list(list, p->sink)) + continue; + if (p->sink->dobj.private) { ret = sof_route_setup(sdev, widget, p->sink); if (ret < 0) return ret; } + } } } else { for_each_dapm_widgets(list, i, widget) { if (!widget->dobj.private) continue; - snd_soc_dapm_widget_for_each_source_path(widget, p) + snd_soc_dapm_widget_for_each_source_path(widget, p) { + if (!widget_in_list(list, p->source)) + continue; + if (p->source->dobj.private) { ret = sof_route_setup(sdev, p->source, widget); if (ret < 0) return ret; } + } } } @@ -264,18 +307,20 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, } static void -sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget) +sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + struct snd_soc_dapm_widget_list *list) { - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; + const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; /* skip if the widget is in use or if it is already unprepared */ if (!swidget || !swidget->prepared || swidget->use_count > 0) goto sink_unprepare; - if (widget_ops[widget->id].ipc_unprepare) + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + if (widget_ops && widget_ops[widget->id].ipc_unprepare) /* unprepare the source widget */ widget_ops[widget->id].ipc_unprepare(swidget); @@ -284,9 +329,11 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg sink_unprepare: /* unprepare all widgets in the sink paths */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!widget_in_list(list, p->sink)) + continue; if (!p->walking && p->sink->dobj.private) { p->walking = true; - sof_unprepare_widgets_in_path(sdev, p->sink); + sof_unprepare_widgets_in_path(sdev, p->sink, list); p->walking = false; } } @@ -296,14 +343,19 @@ static int sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, - struct snd_pcm_hw_params *pipeline_params, int dir) + struct snd_pcm_hw_params *pipeline_params, int dir, + struct snd_soc_dapm_widget_list *list) { - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; + const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; int ret; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + if (!widget_ops) + return 0; + if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) goto sink_prepare; @@ -320,10 +372,13 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget sink_prepare: /* prepare all widgets in the sink paths */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!widget_in_list(list, p->sink)) + continue; if (!p->walking && p->sink->dobj.private) { p->walking = true; ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, - platform_params, pipeline_params, dir); + platform_params, pipeline_params, dir, + list); p->walking = false; if (ret < 0) { /* unprepare the source widget */ @@ -345,27 +400,28 @@ sink_prepare: * (DAI type for capture, AIF type for playback) */ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - int dir) + int dir, struct snd_sof_pcm *spcm) { + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_path *p; int err; int ret = 0; - /* free all widgets even in case of error to keep use counts balanced */ + if (widget->dobj.private) { + err = sof_widget_free(sdev, widget->dobj.private); + if (err < 0) + ret = err; + } + + /* free all widgets in the sink paths even in case of error to keep use counts balanced */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { - if (!p->walking && p->sink->dobj.private && widget->dobj.private) { - p->walking = true; - if (WIDGET_IS_AIF_OR_DAI(widget->id)) { - err = sof_widget_free(sdev, widget->dobj.private); - if (err < 0) - ret = err; - } + if (!p->walking) { + if (!widget_in_list(list, p->sink)) + continue; - err = sof_widget_free(sdev, p->sink->dobj.private); - if (err < 0) - ret = err; + p->walking = true; - err = sof_free_widgets_in_path(sdev, p->sink, dir); + err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm); if (err < 0) ret = err; p->walking = false; @@ -381,37 +437,58 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap * The error path in this function ensures that all successfully set up widgets getting freed. */ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - int dir) + int dir, struct snd_sof_pcm *spcm) { + struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_sof_pipeline *spipe; struct snd_soc_dapm_path *p; int ret; + if (swidget) { + int i; + + ret = sof_widget_setup(sdev, widget->dobj.private); + if (ret < 0) + return ret; + + /* skip populating the pipe_widgets array if it is NULL */ + if (!pipeline_list->pipelines) + goto sink_setup; + + /* + * Add the widget's pipe_widget to the list of pipelines to be triggered if not + * already in the list. This will result in the pipelines getting added in the + * order source to sink. + */ + for (i = 0; i < pipeline_list->count; i++) { + spipe = pipeline_list->pipelines[i]; + if (spipe == swidget->spipe) + break; + } + + if (i == pipeline_list->count) { + pipeline_list->count++; + pipeline_list->pipelines[i] = swidget->spipe; + } + } + +sink_setup: snd_soc_dapm_widget_for_each_sink_path(widget, p) { - if (!p->walking && p->sink->dobj.private && widget->dobj.private) { - p->walking = true; - if (WIDGET_IS_AIF_OR_DAI(widget->id)) { - ret = sof_widget_setup(sdev, widget->dobj.private); - if (ret < 0) - goto out; - } + if (!p->walking) { + if (!widget_in_list(list, p->sink)) + continue; - ret = sof_widget_setup(sdev, p->sink->dobj.private); - if (ret < 0) { - if (WIDGET_IS_AIF_OR_DAI(widget->id)) - sof_widget_free(sdev, widget->dobj.private); - goto out; - } + p->walking = true; - ret = sof_set_up_widgets_in_path(sdev, p->sink, dir); - if (ret < 0) { - if (WIDGET_IS_AIF_OR_DAI(widget->id)) - sof_widget_free(sdev, widget->dobj.private); - sof_widget_free(sdev, p->sink->dobj.private); - } -out: + ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm); p->walking = false; - if (ret < 0) + if (ret < 0) { + if (swidget) + sof_widget_free(sdev, swidget); return ret; + } } } @@ -419,16 +496,20 @@ out: } static int -sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, +sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir, enum sof_widget_op op) { + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; char *str; int ret = 0; int i; + if (!list) + return 0; + for_each_dapm_widgets(list, i, widget) { /* starting widget for playback is AIF type */ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) @@ -440,11 +521,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l switch (op) { case SOF_WIDGET_SETUP: - ret = sof_set_up_widgets_in_path(sdev, widget, dir); + ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm); str = "set up"; break; case SOF_WIDGET_FREE: - ret = sof_free_widgets_in_path(sdev, widget, dir); + ret = sof_free_widgets_in_path(sdev, widget, dir, spcm); str = "free"; break; case SOF_WIDGET_PREPARE: @@ -460,12 +541,12 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l */ memcpy(&pipeline_params, fe_params, sizeof(*fe_params)); - ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, - platform_params, &pipeline_params, dir); + ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params, + &pipeline_params, dir, list); break; } case SOF_WIDGET_UNPREPARE: - sof_unprepare_widgets_in_path(sdev, widget); + sof_unprepare_widgets_in_path(sdev, widget, list); break; default: dev_err(sdev->dev, "Invalid widget op %d\n", op); @@ -485,7 +566,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_sof_platform_stream_params *platform_params, int dir) { - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; int i, ret; @@ -498,16 +579,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, * Prepare widgets for set up. The prepare step is used to allocate memory, assign * instance ID and pick the widget configuration based on the runtime PCM params. */ - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, SOF_WIDGET_PREPARE); if (ret < 0) return ret; /* Set up is used to send the IPC to the DSP to create the widget */ - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, SOF_WIDGET_SETUP); if (ret < 0) { - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, SOF_WIDGET_UNPREPARE); return ret; } @@ -524,11 +605,20 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, for_each_dapm_widgets(list, i, widget) { struct snd_sof_widget *swidget = widget->dobj.private; struct snd_sof_widget *pipe_widget; + struct snd_sof_pipeline *spipe; if (!swidget) continue; - pipe_widget = swidget->pipe_widget; + spipe = swidget->spipe; + if (!spipe) { + dev_err(sdev->dev, "no pipeline found for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto widget_free; + } + + pipe_widget = spipe->pipe_widget; if (!pipe_widget) { dev_err(sdev->dev, "error: no pipeline widget found for %s\n", swidget->widget->name); @@ -536,13 +626,13 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, goto widget_free; } - if (pipe_widget->complete) + if (spipe->complete) continue; - if (ipc_tplg_ops->pipeline_complete) { - pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget); - if (pipe_widget->complete < 0) { - ret = pipe_widget->complete; + if (tplg_ops && tplg_ops->pipeline_complete) { + spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget); + if (spipe->complete < 0) { + ret = spipe->complete; goto widget_free; } } @@ -551,15 +641,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; widget_free: - sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir, + sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, SOF_WIDGET_FREE); - sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); return ret; } int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) { + struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; int ret; @@ -568,14 +659,16 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int return 0; /* send IPC to free widget in the DSP */ - ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE); + ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); /* unprepare the widget */ - sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); snd_soc_dapm_dai_free_widgets(&list); spcm->stream[dir].list = NULL; + pipeline_list->count = 0; + return ret; } @@ -628,11 +721,11 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); int ret; /* Send PCM_FREE IPC to reset pipeline */ - if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { ret = pcm_ops->hw_free(sdev->component, substream); if (ret < 0) return ret; @@ -762,13 +855,13 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* use the tplg configured mclk if existed */ if (!dai) return 0; - if (tplg_ops->dai_get_clk) + if (tplg_ops && tplg_ops->dai_get_clk) return tplg_ops->dai_get_clk(sdev, dai, clk_type); return 0; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 29cf951e3526..e0579af9d281 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -42,6 +42,7 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) #define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out) #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) +#define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer) #define SOF_DAI_CLK_INTEL_SSP_MCLK 0 #define SOF_DAI_CLK_INTEL_SSP_BCLK 1 @@ -85,6 +86,7 @@ struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control; struct snd_sof_dai; +struct snd_sof_pcm; struct snd_sof_dai_config_data { int dai_index; @@ -97,6 +99,11 @@ struct snd_sof_dai_config_data { * @hw_free: Function pointer for hw_free * @trigger: Function pointer for trigger * @dai_link_fixup: Function pointer for DAI link fixup + * @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating + * additional memory in the SOF PCM stream structure + * @pcm_free: Function pointer for PCM free that can be used for freeing any + * additional memory in the SOF PCM stream structure + * @delay: Function pointer for pcm delay calculation */ struct sof_ipc_pcm_ops { int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, @@ -106,6 +113,10 @@ struct sof_ipc_pcm_ops { int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd); int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); + int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); + void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); + snd_pcm_sframes_t (*delay)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); }; /** @@ -166,7 +177,7 @@ struct sof_ipc_tplg_widget_ops { * initialized to 0. * @control: Pointer to the IPC-specific ops for topology kcontrol IO * @route_setup: Function pointer for setting up pipeline connections - * @route_free: Optional op for freeing pipeline connections. + * @route_free: Function pointer for freeing pipeline connections. * @token_list: List of all tokens supported by the IPC version. The size of the token_list * array should be SOF_TOKEN_COUNT. The unused elements in the array will be * initialized to 0. @@ -179,7 +190,10 @@ struct sof_ipc_tplg_widget_ops { * @dai_get_clk: Function pointer for getting the DAI clock setting * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines - * @parse_manifest: Optional function pointer for ipc4 specific parsing of topology manifest + * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest + * @link_setup: Function pointer for IPC-specific DAI link set up + * + * Note: function pointers (ops) are optional */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -199,6 +213,7 @@ struct sof_ipc_tplg_ops { int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*parse_manifest)(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man); + int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link); }; /** struct snd_sof_tuple - Tuple info @@ -274,6 +289,16 @@ struct sof_token_info { int count; }; +/** + * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream + * @count: number of pipeline widgets in the @pipe_widgets array + * @pipelines: array of pipelines + */ +struct snd_sof_pcm_stream_pipeline_list { + u32 count; + struct snd_sof_pipeline **pipelines; +}; + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; @@ -289,6 +314,10 @@ struct snd_sof_pcm_stream { * active or not while suspending the stream */ bool suspend_ignored; + struct snd_sof_pcm_stream_pipeline_list pipeline_list; + + /* used by IPC implementation and core does not touch it */ + void *private; }; /* ALSA SOF PCM device */ @@ -361,16 +390,20 @@ struct snd_sof_widget { int comp_id; int pipeline_id; /* - * complete flag is used to indicate that pipeline set up is complete for scheduler type - * widgets. It is unused for all other widget types. - */ - int complete; - /* * the prepared flag is used to indicate that a widget has been prepared for getting set * up in the DSP. */ bool prepared; - int use_count; /* use_count will be protected by the PCM mutex held by the core */ + + struct mutex setup_mutex; /* to protect the swidget setup and free operations */ + + /* + * use_count is protected by the PCM mutex held by the core and the + * setup_mutex against non stream domain races (kcontrol access for + * example) + */ + int use_count; + int core; int id; /* id is the DAPM widget type */ /* @@ -391,7 +424,7 @@ struct snd_sof_widget { struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ - struct snd_sof_widget *pipe_widget; + struct snd_sof_pipeline *spipe; void *module_info; const guid_t uuid; @@ -429,6 +462,22 @@ struct snd_sof_widget { void *private; /* core does not touch this */ }; +/** struct snd_sof_pipeline - ASoC SOF pipeline + * @pipe_widget: Pointer to the pipeline widget + * @started_count: Count of number of PCM's that have started this pipeline + * @paused_count: Count of number of PCM's that have started and have currently paused this + pipeline + * @complete: flag used to indicate that pipeline set up is complete. + * @list: List item in sdev pipeline_list + */ +struct snd_sof_pipeline { + struct snd_sof_widget *pipe_widget; + int started_count; + int paused_count; + int complete; + struct list_head list; +}; + /* ASoC SOF DAPM route */ struct snd_sof_route { struct snd_soc_component *scomp; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6760fd895950..5f919162a555 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -20,6 +20,8 @@ #include <uapi/sound/sof/fw.h> #include <sound/sof/ext_manifest.h> +struct snd_sof_pcm_stream; + /* Flag definitions used in sof_core_debug (sof_debug module parameter) */ #define SOF_DBG_ENABLE_TRACE BIT(0) #define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */ @@ -113,6 +115,7 @@ struct sof_compr_stream { u32 sampling_rate; u16 channels; u16 sample_container_bytes; + size_t posn_offset; }; struct snd_sof_dev; @@ -245,14 +248,23 @@ struct snd_sof_dsp_ops { /* pcm ack */ int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + /* + * optional callback to retrieve the link DMA position for the substream + * when the position is not reported in the shared SRAM windows but + * instead from a host-accessible hardware counter. + */ + u64 (*get_stream_position)(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); /* optional */ + /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz); /* mandatory */ /* host side configuration of the stream's data offset in stream mailbox area */ int (*set_stream_data_offset)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset); /* optional */ /* pre/post firmware run */ @@ -413,11 +425,13 @@ struct sof_ipc_fw_tracing_ops { * @ctx_save: Optional function pointer for context save * @ctx_restore: Optional function pointer for context restore * @set_core_state: Optional function pointer for turning on/off a DSP core + * @set_pm_gate: Optional function pointer for pm gate settings */ struct sof_ipc_pm_ops { int (*ctx_save)(struct snd_sof_dev *sdev); int (*ctx_restore)(struct snd_sof_dev *sdev); int (*set_core_state)(struct snd_sof_dev *sdev, int core_idx, bool on); + int (*set_pm_gate)(struct snd_sof_dev *sdev, u32 flags); }; /** @@ -444,7 +458,7 @@ struct sof_ipc_pcm_ops; * @pm: Pointer to PM ops * @pcm: Pointer to PCM ops * @fw_loader: Pointer to Firmware Loader ops - * @fw_tracing: Pointer to Firmware tracing ops + * @fw_tracing: Optional pointer to Firmware tracing ops * * @init: Optional pointer for IPC related initialization * @exit: Optional pointer for IPC related cleanup @@ -502,6 +516,10 @@ struct snd_sof_ipc { const struct sof_ipc_ops *ops; }; +/* Helper to retrieve the IPC ops */ +#define sof_ipc_get_ops(sdev, ops_name) \ + (((sdev)->ipc && (sdev)->ipc->ops) ? (sdev)->ipc->ops->ops_name : NULL) + /* * SOF Device Level. */ @@ -541,6 +559,7 @@ struct snd_sof_dev { /* IPC */ struct snd_sof_ipc *ipc; + struct snd_sof_mailbox fw_info_box; /* FW shared memory */ struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */ struct snd_sof_mailbox host_box; /* Host initiated IPC */ struct snd_sof_mailbox stream_box; /* Stream position update */ @@ -571,6 +590,7 @@ struct snd_sof_dev { struct list_head pcm_list; struct list_head kcontrol_list; struct list_head widget_list; + struct list_head pipeline_list; struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; @@ -757,10 +777,10 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, void *dest, size_t size); int sof_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz); int sof_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset); int sof_stream_pcm_open(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 5f1ceeea893a..216b454f6b94 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -19,6 +19,7 @@ #include "ops.h" #include "sof-priv.h" +#include "sof-audio.h" struct sof_stream { size_t posn_offset; @@ -26,19 +27,33 @@ struct sof_stream { /* Mailbox-based Generic IPC implementation */ int sof_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, void *p, size_t sz) { - if (!substream || !sdev->stream_box.size) { + if (!sps || !sdev->stream_box.size) { snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); } else { - struct sof_stream *stream = substream->runtime->private_data; + size_t posn_offset; - /* The stream might already be closed */ - if (!stream) - return -ESTRPIPE; + if (sps->substream) { + struct sof_stream *stream = sps->substream->runtime->private_data; - snd_sof_dsp_mailbox_read(sdev, stream->posn_offset, p, sz); + /* The stream might already be closed */ + if (!stream) + return -ESTRPIPE; + + posn_offset = stream->posn_offset; + } else { + + struct sof_compr_stream *sstream = sps->cstream->runtime->private_data; + + if (!sstream) + return -ESTRPIPE; + + posn_offset = sstream->posn_offset; + } + + snd_sof_dsp_mailbox_read(sdev, posn_offset, p, sz); } return 0; @@ -46,20 +61,32 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev, EXPORT_SYMBOL(sof_ipc_msg_data); int sof_set_stream_data_offset(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, + struct snd_sof_pcm_stream *sps, size_t posn_offset) { - struct sof_stream *stream = substream->runtime->private_data; - /* check if offset is overflow or it is not aligned */ if (posn_offset > sdev->stream_box.size || posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) return -EINVAL; - stream->posn_offset = sdev->stream_box.offset + posn_offset; + posn_offset += sdev->stream_box.offset; + + if (sps->substream) { + struct sof_stream *stream = sps->substream->runtime->private_data; + + stream->posn_offset = posn_offset; + dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", + sps->substream->stream, posn_offset); + } else if (sps->cstream) { + struct sof_compr_stream *sstream = sps->cstream->runtime->private_data; - dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", - substream->stream, stream->posn_offset); + sstream->posn_offset = posn_offset; + dev_dbg(sdev->dev, "compr: stream dir %d, posn mailbox offset is %zu", + sps->cstream->direction, posn_offset); + } else { + dev_err(sdev->dev, "No stream opened"); + return -EINVAL; + } return 0; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c668bd9d21ec..4a62ccc71fcb 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -54,11 +54,16 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so size_t object_size, int token_instance_num) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_token_info *token_list; const struct sof_topology_token *tokens; int i, j; + token_list = tplg_ops ? tplg_ops->token_list : NULL; + /* nothing to do if token_list is NULL */ + if (!token_list) + return 0; + if (token_list[token_id].count < 0) { dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id); return -EINVAL; @@ -263,9 +268,9 @@ static int set_up_volume_table(struct snd_sof_control *scontrol, { struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (tplg_ops->control->set_up_volume_table) + if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table) return tplg_ops->control->set_up_volume_table(scontrol, tlv, size); dev_err(scomp->dev, "Mandatory op %s not set\n", __func__); @@ -490,13 +495,14 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ int array_size, u32 token_id, int token_instance_num, struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) { - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_token_info *token_list; const struct sof_topology_token *tokens; int found = 0; int num_tokens, asize; int i, j; + token_list = tplg_ops ? tplg_ops->token_list : NULL; /* nothing to do if token_list is NULL */ if (!token_list) return 0; @@ -1015,14 +1021,14 @@ static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_control *scontrol = dobj->private; int ret = 0; dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name); - if (ipc_tplg_ops->control_free) { - ret = ipc_tplg_ops->control_free(sdev, scontrol); + if (tplg_ops && tplg_ops->control_free) { + ret = tplg_ops->control_free(sdev, scontrol); if (ret < 0) dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name); } @@ -1049,6 +1055,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *cpu_dai; + int stream; int i; if (!w->sname) { @@ -1056,62 +1063,41 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return -EINVAL; } + if (w->id == snd_soc_dapm_dai_out) + stream = SNDRV_PCM_STREAM_CAPTURE; + else if (w->id == snd_soc_dapm_dai_in) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + goto end; + list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || strcmp(w->sname, rtd->dai_link->stream_name)) continue; - switch (w->id) { - case snd_soc_dapm_dai_out: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - /* - * Please create DAI widget in the right order - * to ensure BE will connect to the right DAI - * widget. - */ - if (!cpu_dai->capture_widget) { - cpu_dai->capture_widget = w; - break; - } - } - if (i == rtd->dai_link->num_cpus) { - dev_err(scomp->dev, "error: can't find BE for DAI %s\n", - w->name); - - return -EINVAL; - } - dai->name = rtd->dai_link->name; - dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", - w->name, rtd->dai_link->name); - break; - case snd_soc_dapm_dai_in: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - /* - * Please create DAI widget in the right order - * to ensure BE will connect to the right DAI - * widget. - */ - if (!cpu_dai->playback_widget) { - cpu_dai->playback_widget = w; - break; - } + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + /* + * Please create DAI widget in the right order + * to ensure BE will connect to the right DAI + * widget. + */ + if (!snd_soc_dai_get_widget(cpu_dai, stream)) { + snd_soc_dai_set_widget(cpu_dai, stream, w); + break; } - if (i == rtd->dai_link->num_cpus) { - dev_err(scomp->dev, "error: can't find BE for DAI %s\n", - w->name); + } + if (i == rtd->dai_link->num_cpus) { + dev_err(scomp->dev, "error: can't find BE for DAI %s\n", w->name); - return -EINVAL; - } - dai->name = rtd->dai_link->name; - dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", - w->name, rtd->dai_link->name); - break; - default: - break; + return -EINVAL; } - } + dai->name = rtd->dai_link->name; + dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + } +end: /* check we have a connection */ if (!dai->name) { dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n", @@ -1128,37 +1114,29 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *cpu_dai; - int i; + int i, stream; if (!w->sname) return; + if (w->id == snd_soc_dapm_dai_out) + stream = SNDRV_PCM_STREAM_CAPTURE; + else if (w->id == snd_soc_dapm_dai_in) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + return; + list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || strcmp(w->sname, rtd->dai_link->stream_name)) continue; - switch (w->id) { - case snd_soc_dapm_dai_out: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (cpu_dai->capture_widget == w) { - cpu_dai->capture_widget = NULL; - break; - } + for_each_rtd_cpu_dais(rtd, i, cpu_dai) + if (snd_soc_dai_get_widget(cpu_dai, stream) == w) { + snd_soc_dai_set_widget(cpu_dai, stream, NULL); + break; } - break; - case snd_soc_dapm_dai_in: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (cpu_dai->playback_widget == w) { - cpu_dai->playback_widget = NULL; - break; - } - } - break; - default: - break; - } } } @@ -1201,12 +1179,17 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s enum sof_tokens *object_token_list, int count) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &tw->priv; + const struct sof_token_info *token_list; int num_tuples = 0; int ret, i; + token_list = tplg_ops ? tplg_ops->token_list : NULL; + /* nothing to do if token_list is NULL */ + if (!token_list) + return 0; + if (count > 0 && !object_token_list) { dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name); return -EINVAL; @@ -1375,13 +1358,13 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_tplg_private *priv = &tw->priv; + enum sof_tokens *token_list = NULL; struct snd_sof_widget *swidget; struct snd_sof_dai *dai; - enum sof_tokens *token_list; - int token_list_size; + int token_list_size = 0; int ret = 0; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); @@ -1391,10 +1374,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->scomp = scomp; swidget->widget = w; swidget->comp_id = sdev->next_comp_id++; - swidget->complete = 0; swidget->id = w->id; swidget->pipeline_id = index; swidget->private = NULL; + mutex_init(&swidget->setup_mutex); + ida_init(&swidget->src_queue_ida); ida_init(&swidget->sink_queue_ida); @@ -1440,8 +1424,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->num_sink_pins, swidget->num_source_pins, strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none"); - token_list = widget_ops[w->id].token_list; - token_list_size = widget_ops[w->id].token_list_size; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + if (widget_ops) { + token_list = widget_ops[w->id].token_list; + token_list_size = widget_ops[w->id].token_list_size; + } /* handle any special case widgets */ switch (w->id) { @@ -1525,7 +1512,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, /* bind widget to external event */ if (tw->event_type) { - if (widget_ops[w->id].bind_event) { + if (widget_ops && widget_ops[w->id].bind_event) { ret = widget_ops[w->id].bind_event(scomp, swidget, le16_to_cpu(tw->event_type)); if (ret) { @@ -1539,6 +1526,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } } + /* create and add pipeline for scheduler type widgets */ + if (w->id == snd_soc_dapm_scheduler) { + struct snd_sof_pipeline *spipe; + + spipe = kzalloc(sizeof(*spipe), GFP_KERNEL); + if (!spipe) { + kfree(swidget->private); + kfree(swidget->tuples); + kfree(swidget); + return -ENOMEM; + } + + spipe->pipe_widget = swidget; + swidget->spipe = spipe; + list_add(&spipe->list, &sdev->pipeline_list); + } + w->dobj.private = swidget; list_add(&swidget->list, &sdev->widget_list); return ret; @@ -1565,8 +1569,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_widget_ops *widget_ops; const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -1594,6 +1598,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp, sof_disconnect_dai_widget(scomp, widget); break; + case snd_soc_dapm_scheduler: + { + struct snd_sof_pipeline *spipe = swidget->spipe; + + list_del(&spipe->list); + kfree(spipe); + swidget->spipe = NULL; + break; + } default: break; } @@ -1626,7 +1639,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp, out: /* free IPC related data */ - if (widget_ops[swidget->id].ipc_free) + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + if (widget_ops && widget_ops[swidget->id].ipc_free) widget_ops[swidget->id].ipc_free(swidget); ida_destroy(&swidget->src_queue_ida); @@ -1654,6 +1668,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; @@ -1681,6 +1696,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, spcm->pcm = *pcm; dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); + /* perform pcm set op */ + if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) { + ret = ipc_pcm_ops->pcm_setup(sdev, spcm); + if (ret < 0) + return ret; + } + dai_drv->dobj.private = spcm; list_add(&spcm->list, &sdev->pcm_list); @@ -1758,6 +1780,8 @@ free_playback_tables: static int sof_dai_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm = dobj->private; /* free PCM DMA pages */ @@ -1767,6 +1791,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp, if (spcm->pcm.capture) snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); + /* perform pcm free op */ + if (ipc_pcm_ops && ipc_pcm_ops->pcm_free) + ipc_pcm_ops->pcm_free(sdev, spcm); + /* remove from list and free spcm */ list_del(&spcm->list); kfree(spcm); @@ -1784,9 +1812,9 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ struct snd_soc_tplg_link_config *cfg) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &cfg->priv; + const struct sof_token_info *token_list; struct snd_sof_dai_link *slink; u32 token_id = 0; int num_tuples = 0; @@ -1798,26 +1826,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ } link->platforms->name = dev_name(scomp->dev); - /* - * Set nonatomic property for FE dai links as their trigger action - * involves IPC's. - */ + if (tplg_ops && tplg_ops->link_setup) { + ret = tplg_ops->link_setup(sdev, link); + if (ret < 0) + return ret; + } + + /* Set nonatomic property for FE dai links as their trigger action involves IPC's */ if (!link->no_pcm) { link->nonatomic = true; - - /* - * set default trigger order for all links. Exceptions to - * the rule will be handled in sof_pcm_dai_link_fixup() - * For playback, the sequence is the following: start FE, - * start BE, stop BE, stop FE; for Capture the sequence is - * inverted start BE, start FE, stop FE, stop BE - */ - link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = - SND_SOC_DPCM_TRIGGER_PRE; - link->trigger[SNDRV_PCM_STREAM_CAPTURE] = - SND_SOC_DPCM_TRIGGER_POST; - - /* nothing more to do for FE dai links */ return 0; } @@ -1856,6 +1873,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ return ret; } + token_list = tplg_ops ? tplg_ops->token_list : NULL; if (!token_list) goto out; @@ -2063,18 +2081,19 @@ err: } /** - * sof_set_pipe_widget - Set pipe_widget for a component + * sof_set_widget_pipeline - Set pipeline for a component * @sdev: pointer to struct snd_sof_dev - * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler + * @spipe: pointer to struct snd_sof_pipeline * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget * * Return: 0 if successful, -EINVAL on error. * The function checks if @swidget is associated with any volatile controls. If so, setting * the dynamic_pipeline_widget is disallowed. */ -static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget, - struct snd_sof_widget *swidget) +static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe, + struct snd_sof_widget *swidget) { + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_control *scontrol; if (pipe_widget->dynamic_pipeline_widget) { @@ -2089,8 +2108,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget * } } - /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ - swidget->pipe_widget = pipe_widget; + /* set the pipeline and apply the dynamic_pipeline_widget_flag */ + swidget->spipe = spipe; swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; return 0; @@ -2100,16 +2119,19 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget * static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget, *comp_swidget; - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_sof_control *scontrol; + struct snd_sof_pipeline *spipe; int ret; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + /* first update all control IPC structures based on the IPC version */ - if (ipc_tplg_ops->control_setup) + if (tplg_ops && tplg_ops->control_setup) list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - ret = ipc_tplg_ops->control_setup(sdev, scontrol); + ret = tplg_ops->control_setup(sdev, scontrol); if (ret < 0) { dev_err(sdev->dev, "failed updating IPC struct for control %s\n", scontrol->name); @@ -2123,7 +2145,7 @@ static int sof_complete(struct snd_soc_component *scomp) * associated memories. */ list_for_each_entry(swidget, &sdev->widget_list, list) { - if (widget_ops[swidget->id].ipc_setup) { + if (widget_ops && widget_ops[swidget->id].ipc_setup) { ret = widget_ops[swidget->id].ipc_setup(swidget); if (ret < 0) { dev_err(sdev->dev, "failed updating IPC struct for %s\n", @@ -2134,36 +2156,35 @@ static int sof_complete(struct snd_soc_component *scomp) } /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - switch (swidget->id) { - case snd_soc_dapm_scheduler: - /* - * Apply the dynamic_pipeline_widget flag and set the pipe_widget field - * for all widgets that have the same pipeline ID as the scheduler widget - */ - list_for_each_entry(comp_swidget, &sdev->widget_list, list) - if (comp_swidget->pipeline_id == swidget->pipeline_id) { - ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); - if (ret < 0) - return ret; - } - break; - default: - break; - } + list_for_each_entry(spipe, &sdev->pipeline_list, list) { + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + + /* + * Apply the dynamic_pipeline_widget flag and set the pipe_widget field + * for all widgets that have the same pipeline ID as the scheduler widget. + * Skip the scheduler widgets as they have their pipeline set during widget_ready + */ + list_for_each_entry(comp_swidget, &sdev->widget_list, list) + if (comp_swidget->widget->id != snd_soc_dapm_scheduler && + comp_swidget->pipeline_id == pipe_widget->pipeline_id) { + ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget); + if (ret < 0) + return ret; + } } /* verify topology components loading including dynamic pipelines */ if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { - if (ipc_tplg_ops->set_up_all_pipelines && ipc_tplg_ops->tear_down_all_pipelines) { - ret = ipc_tplg_ops->set_up_all_pipelines(sdev, true); + if (tplg_ops && tplg_ops->set_up_all_pipelines && + tplg_ops->tear_down_all_pipelines) { + ret = tplg_ops->set_up_all_pipelines(sdev, true); if (ret < 0) { dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", ret); return ret; } - ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true); + ret = tplg_ops->tear_down_all_pipelines(sdev, true); if (ret < 0) { dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", ret); @@ -2173,8 +2194,8 @@ static int sof_complete(struct snd_soc_component *scomp) } /* set up static pipelines */ - if (ipc_tplg_ops->set_up_all_pipelines) - return ipc_tplg_ops->set_up_all_pipelines(sdev, false); + if (tplg_ops && tplg_ops->set_up_all_pipelines) + return tplg_ops->set_up_all_pipelines(sdev, false); return 0; } @@ -2184,10 +2205,10 @@ static int sof_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - if (ipc_tplg_ops->parse_manifest) - return ipc_tplg_ops->parse_manifest(scomp, index, man); + if (tplg_ops && tplg_ops->parse_manifest) + return tplg_ops->parse_manifest(scomp, index, man); return 0; } diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 6f662642d611..b2ab51e5214a 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -6,19 +6,21 @@ int sof_fw_trace_init(struct snd_sof_dev *sdev) { - if (!sdev->ipc->ops->fw_tracing) { + const struct sof_ipc_fw_tracing_ops *fw_tracing = sof_ipc_get_ops(sdev, fw_tracing); + + if (!fw_tracing) { dev_info(sdev->dev, "Firmware tracing is not available\n"); sdev->fw_trace_is_supported = false; return 0; } - return sdev->ipc->ops->fw_tracing->init(sdev); + return fw_tracing->init(sdev); } void sof_fw_trace_free(struct snd_sof_dev *sdev) { - if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing) + if (!sdev->fw_trace_is_supported) return; if (sdev->ipc->ops->fw_tracing->free) diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index fb107c5790ad..469373d1bb41 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -244,7 +244,8 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); host->dma_params_tx.filter_data = &host->dma_params; - dai->playback_dma_data = &host->dma_params_tx; + + snd_soc_dai_dma_data_set_playback(dai, &host->dma_params_tx); return snd_soc_add_dai_controls(dai, spdif_out_controls, ARRAY_SIZE(spdif_out_controls)); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index b6712a3d1fa1..73a683d45526 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -144,7 +144,7 @@ config SND_SOC_TEGRA210_SFC Config to enable the Sampling Frequency Converter (SFC) which converts the sampling frequency of input signal to another frequency. It supports sampling frequency conversion of streams - upto 2 channels (stereo). + up to 2 channels (stereo). Say Y or M if you want to add support for Tegra210 SFC module. config SND_SOC_TEGRA210_AMX @@ -171,7 +171,7 @@ config SND_SOC_TEGRA210_MIXER tristate "Tegra210 Mixer module" help Config to enable the Mixer module which can help to mix multiple - audio streams. It supports mixing of upto 10 input streams, + audio streams. It supports mixing of up to 10 input streams, where each stream can contain maximum of 8 channels. It supports 5 output each of which can be a mix of any combination of 10 input streams. diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 87facfbcdd11..d23d88a10899 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -212,8 +212,8 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) { struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &ac97->capture_dma_data; - dai->playback_dma_data = &ac97->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &ac97->playback_dma_data, + &ac97->capture_dma_data); return 0; } diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index fff0cd6588f5..d37a9f2603e8 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -256,8 +256,8 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) { struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &i2s->capture_dma_data; - dai->playback_dma_data = &i2s->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); return 0; } diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index ca7b222e07d0..86bef54dfdf2 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -236,8 +236,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) { struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); - dai->capture_dma_data = NULL; - dai->playback_dma_data = &spdif->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &spdif->playback_dma_data, NULL); return 0; } diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 1a2e868a6220..100a2b6e6063 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -544,8 +544,8 @@ static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) { struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &admaif->capture_dma_data[dai->id]; - dai->playback_dma_data = &admaif->playback_dma_data[dai->id]; + snd_soc_dai_init_dma_data(dai, &admaif->playback_dma_data[dai->id], + &admaif->capture_dma_data[dai->id]); return 0; } diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 10cd37096fb3..c26f960c6afd 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -297,8 +297,8 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &i2s->capture_dma_data; - dai->playback_dma_data = &i2s->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); return 0; } diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index e6e77a5f3c1e..3ccd0cfca008 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -614,9 +614,10 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + int stream; - dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + for_each_pcm_streams(stream) + snd_soc_dai_dma_data_set(dai, stream, &dev->dma_data[stream]); return 0; } diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index f5ac2ab77f5b..578254549d2d 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1699,9 +1699,10 @@ static void davinci_mcasp_init_iec958_status(struct davinci_mcasp *mcasp) static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + int stream; - dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + for_each_pcm_streams(stream) + snd_soc_dai_dma_data_set(dai, stream, &mcasp->dma_data[stream]); if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) { davinci_mcasp_init_iec958_status(mcasp); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index fdd55d772b8e..325e75e96136 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -109,11 +109,9 @@ static int mop500_probe(struct platform_device *pdev) mop500_card.dev = &pdev->dev; - if (np) { - ret = mop500_of_probe(pdev, np); - if (ret) - return ret; - } + ret = mop500_of_probe(pdev, np); + if (ret) + return ret; dev_dbg(&pdev->dev, "%s: Card %s: Set platform drvdata.\n", __func__, mop500_card.name); diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 9d99ea6d7f30..6fb1a5c207bc 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -15,7 +15,6 @@ #include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/mfd/dbx500-prcmu.h> -#include <linux/platform_data/asoc-ux500-msp.h> #include <sound/soc.h> #include <sound/soc-dai.h> @@ -671,8 +670,8 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) if (!capture_dma_data) return -ENOMEM; - playback_dma_data->addr = drvdata->msp->playback_dma_data.tx_rx_addr; - capture_dma_data->addr = drvdata->msp->capture_dma_data.tx_rx_addr; + playback_dma_data->addr = drvdata->msp->tx_rx_addr; + capture_dma_data->addr = drvdata->msp->tx_rx_addr; playback_dma_data->maxburst = 4; capture_dma_data->maxburst = 4; @@ -682,26 +681,6 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) return 0; } -static int ux500_msp_dai_probe(struct snd_soc_dai *dai) -{ - struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); - struct msp_i2s_platform_data *pdata = dai->dev->platform_data; - int ret; - - if (!pdata) { - ret = ux500_msp_dai_of_probe(dai); - return ret; - } - - drvdata->msp->playback_dma_data.data_size = drvdata->slot_width; - drvdata->msp->capture_dma_data.data_size = drvdata->slot_width; - - snd_soc_dai_init_dma_data(dai, - &drvdata->msp->playback_dma_data, - &drvdata->msp->capture_dma_data); - return 0; -} - static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { { .set_sysclk = ux500_msp_dai_set_dai_sysclk, @@ -716,7 +695,7 @@ static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { }; static struct snd_soc_dai_driver ux500_msp_dai_drv = { - .probe = ux500_msp_dai_probe, + .probe = ux500_msp_dai_of_probe, .playback.channels_min = UX500_MSP_MIN_CHANNELS, .playback.channels_max = UX500_MSP_MAX_CHANNELS, .playback.rates = UX500_I2S_RATES, @@ -737,15 +716,8 @@ static const struct snd_soc_component_driver ux500_msp_component = { static int ux500_msp_drv_probe(struct platform_device *pdev) { struct ux500_msp_i2s_drvdata *drvdata; - struct msp_i2s_platform_data *pdata = pdev->dev.platform_data; - struct device_node *np = pdev->dev.of_node; int ret = 0; - if (!pdata && !np) { - dev_err(&pdev->dev, "No platform data or Device Tree found\n"); - return -ENODEV; - } - drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp_i2s_drvdata), GFP_KERNEL); @@ -787,8 +759,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) return ret; } - ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, - pdev->dev.platform_data); + ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp); if (!drvdata->msp) { dev_err(&pdev->dev, "%s: ERROR: Failed to init MSP-struct (%d)!", diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index d113411a19f8..fbfeefa418ca 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/platform_data/asoc-ux500-msp.h> #include <sound/soc.h> @@ -361,20 +360,6 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) __func__, status); } - /* Make sure the correct DMA-directions are configured */ - if ((config->direction & MSP_DIR_RX) && - !msp->capture_dma_data.dma_cfg) { - dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!", - __func__); - return -EINVAL; - } - if ((config->direction == MSP_DIR_TX) && - !msp->playback_dma_data.dma_cfg) { - dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!", - __func__); - return -EINVAL; - } - reg_val_DMACR = readl(msp->registers + MSP_DMACR); if (config->direction & MSP_DIR_RX) reg_val_DMACR |= RX_DMA_ENABLE; @@ -639,62 +624,17 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) } -static int ux500_msp_i2s_of_init_msp(struct platform_device *pdev, - struct ux500_msp *msp, - struct msp_i2s_platform_data **platform_data) -{ - struct msp_i2s_platform_data *pdata; - - *platform_data = devm_kzalloc(&pdev->dev, - sizeof(struct msp_i2s_platform_data), - GFP_KERNEL); - pdata = *platform_data; - if (!pdata) - return -ENOMEM; - - msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, - sizeof(struct stedma40_chan_cfg), - GFP_KERNEL); - if (!msp->playback_dma_data.dma_cfg) - return -ENOMEM; - - msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, - sizeof(struct stedma40_chan_cfg), - GFP_KERNEL); - if (!msp->capture_dma_data.dma_cfg) - return -ENOMEM; - - return 0; -} - int ux500_msp_i2s_init_msp(struct platform_device *pdev, - struct ux500_msp **msp_p, - struct msp_i2s_platform_data *platform_data) + struct ux500_msp **msp_p) { struct resource *res = NULL; - struct device_node *np = pdev->dev.of_node; struct ux500_msp *msp; - int ret; *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); msp = *msp_p; if (!msp) return -ENOMEM; - if (!platform_data) { - if (np) { - ret = ux500_msp_i2s_of_init_msp(pdev, msp, - &platform_data); - if (ret) - return ret; - } else - return -EINVAL; - } else { - msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx; - msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx; - msp->id = platform_data->id; - } - msp->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -704,9 +644,7 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, return -ENOMEM; } - msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR; - msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR; - + msp->tx_rx_addr = res->start + MSP_DR; msp->registers = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (msp->registers == NULL) { diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index d45b5e2831cc..69d4ebc409fc 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -11,7 +11,6 @@ #define UX500_MSP_I2S_H #include <linux/platform_device.h> -#include <linux/platform_data/asoc-ux500-msp.h> #define MSP_INPUT_FREQ_APB 48000000 @@ -463,18 +462,11 @@ struct ux500_msp_config { unsigned int iodelay; }; -struct ux500_msp_dma_params { - unsigned int data_size; - dma_addr_t tx_rx_addr; - struct stedma40_chan_cfg *dma_cfg; -}; - struct ux500_msp { int id; void __iomem *registers; struct device *dev; - struct ux500_msp_dma_params playback_dma_data; - struct ux500_msp_dma_params capture_dma_data; + dma_addr_t tx_rx_addr; enum msp_state msp_state; int def_elem_len; unsigned int dir_busy; @@ -482,10 +474,8 @@ struct ux500_msp { unsigned int f_bitclk; }; -struct msp_i2s_platform_data; int ux500_msp_i2s_init_msp(struct platform_device *pdev, - struct ux500_msp **msp_p, - struct msp_i2s_platform_data *platform_data); + struct ux500_msp **msp_p); void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, struct ux500_msp *msp); int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config); diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index d3802e5ef196..53b5649cfdda 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -13,7 +13,6 @@ #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/slab.h> -#include <linux/platform_data/dma-ste-dma40.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -29,76 +28,17 @@ #define UX500_PLATFORM_PERIODS_MAX 48 #define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) -static const struct snd_pcm_hardware ux500_pcm_hw = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_PAUSE, - .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX, - .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN, - .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX, - .periods_min = UX500_PLATFORM_PERIODS_MIN, - .periods_max = UX500_PLATFORM_PERIODS_MAX, -}; - -static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream) -{ - struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); - u16 per_data_width, mem_data_width; - struct stedma40_chan_cfg *dma_cfg; - struct ux500_msp_dma_params *dma_params; - - dma_params = snd_soc_dai_get_dma_data(dai, substream); - dma_cfg = dma_params->dma_cfg; - - mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - - switch (dma_params->data_size) { - case 32: - per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - case 16: - per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case 8: - per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - break; - default: - per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_cfg->src_info.data_width = mem_data_width; - dma_cfg->dst_info.data_width = per_data_width; - } else { - dma_cfg->src_info.data_width = per_data_width; - dma_cfg->dst_info.data_width = mem_data_width; - } - - return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); -} - static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct msp_i2s_platform_data *pdata = asoc_rtd_to_cpu(rtd, 0)->dev->platform_data; struct snd_dmaengine_dai_dma_data *snd_dma_params; - struct ux500_msp_dma_params *ste_dma_params; dma_addr_t dma_addr; int ret; - if (pdata) { - ste_dma_params = - snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); - dma_addr = ste_dma_params->tx_rx_addr; - } else { - snd_dma_params = - snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); - dma_addr = snd_dma_params->addr; - } + snd_dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + dma_addr = snd_dma_params->addr; ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); if (ret) @@ -118,31 +58,16 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, return 0; } -static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { - .pcm_hardware = &ux500_pcm_hw, - .compat_request_channel = ux500_pcm_request_chan, - .prealloc_buffer_size = 128 * 1024, - .prepare_slave_config = ux500_pcm_prepare_slave_config, -}; - static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = { - .compat_request_channel = ux500_pcm_request_chan, .prepare_slave_config = ux500_pcm_prepare_slave_config, }; int ux500_pcm_register_platform(struct platform_device *pdev) { - const struct snd_dmaengine_pcm_config *pcm_config; - struct device_node *np = pdev->dev.of_node; int ret; - if (np) - pcm_config = &ux500_dmaengine_of_pcm_config; - else - pcm_config = &ux500_dmaengine_pcm_config; - - ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, - SND_DMAENGINE_PCM_FLAG_COMPAT); + ret = snd_dmaengine_pcm_register(&pdev->dev, + &ux500_dmaengine_of_pcm_config, 0); if (ret < 0) { dev_err(&pdev->dev, "%s: ERROR: Failed to register platform '%s' (%d)!\n", diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile index a8c0383878d3..901949db80ad 100644 --- a/tools/testing/selftests/alsa/Makefile +++ b/tools/testing/selftests/alsa/Makefile @@ -8,13 +8,15 @@ LDLIBS += -lasound endif CFLAGS += -L$(OUTPUT) -Wl,-rpath=./ +LDLIBS+=-lpthread + OVERRIDE_TARGETS = 1 TEST_GEN_PROGS := mixer-test pcm-test TEST_GEN_PROGS_EXTENDED := libatest.so -TEST_FILES := conf.d +TEST_FILES := conf.d pcm-test.conf include ../lib.mk diff --git a/tools/testing/selftests/alsa/alsa-local.h b/tools/testing/selftests/alsa/alsa-local.h index 65f197ea9773..de030dc23bd1 100644 --- a/tools/testing/selftests/alsa/alsa-local.h +++ b/tools/testing/selftests/alsa/alsa-local.h @@ -12,6 +12,7 @@ snd_config_t *get_alsalib_config(void); +snd_config_t *conf_load_from_file(const char *filename); void conf_load(void); void conf_free(void); snd_config_t *conf_by_card(int card); @@ -20,5 +21,7 @@ int conf_get_count(snd_config_t *root, const char *key1, const char *key2); const char *conf_get_string(snd_config_t *root, const char *key1, const char *key2, const char *def); long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long def); int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def); +void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2, + const char **array, int array_size, const char *def); #endif /* __ALSA_LOCAL_H */ diff --git a/tools/testing/selftests/alsa/conf.c b/tools/testing/selftests/alsa/conf.c index c7ffc8f04195..d7aafe5a1993 100644 --- a/tools/testing/selftests/alsa/conf.c +++ b/tools/testing/selftests/alsa/conf.c @@ -125,7 +125,7 @@ static int dump_config_tree(snd_config_t *top) snd_output_close(out); } -static snd_config_t *load(const char *filename) +snd_config_t *conf_load_from_file(const char *filename) { snd_config_t *dst; snd_input_t *input; @@ -235,7 +235,7 @@ static bool test_filename1(int card, const char *filename, const char *sysfs_car snd_config_t *config, *sysfs_config, *card_config, *sysfs_card_config, *node; snd_config_iterator_t i, next; - config = load(filename); + config = conf_load_from_file(filename); if (snd_config_search(config, "sysfs", &sysfs_config) || snd_config_get_type(sysfs_config) != SND_CONFIG_TYPE_COMPOUND) ksft_exit_fail_msg("Missing global sysfs block in filename %s\n", filename); @@ -446,3 +446,25 @@ int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int de ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1, key2); return !!ret; } + +void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2, + const char **array, int array_size, const char *def) +{ + snd_config_t *cfg; + char buf[16]; + int ret, index; + + ret = conf_get_by_keys(root, key1, key2, &cfg); + if (ret == -ENOENT) + cfg = NULL; + else if (ret < 0) + ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret)); + for (index = 0; index < array_size; index++) { + if (cfg == NULL) { + array[index] = def; + } else { + sprintf(buf, "%i", index); + array[index] = conf_get_string(cfg, buf, NULL, def); + } + } +} diff --git a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf index 9eca985e0c08..5b40a916295d 100644 --- a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf +++ b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf @@ -39,25 +39,30 @@ card.hda { # pcm.0.0 { PLAYBACK { - # - # Uncomment to override values for specific tests - # - #test_name1 { - # access RW_INTERLEAVED - # format S16_LE - # rate 48000 - # channels 2 - # period_size 512 - # buffer_size 4096 - #} - #test_name2 { - # access RW_INTERLEAVED - # format S16_LE - # rate 48000 - # channels 2 - # period_size 24000 - # buffer_size 192000 - #} + test.time1 { + access RW_INTERLEAVED # can be omitted - default + format S16_LE # can be omitted - default + rate 48000 # can be omitted - default + channels 2 # can be omitted - default + period_size 512 + buffer_size 4096 + } + test.time2 { + access RW_INTERLEAVED + format S16_LE + rate 48000 + channels 2 + period_size 24000 + buffer_size 192000 + } + test.time3 { + access RW_INTERLEAVED + format S16_LE + rate 44100 + channels 2 + period_size 24000 + buffer_size 192000 + } } CAPTURE { # use default tests, check for the presence diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c index f293c7d81009..58b525a4a32c 100644 --- a/tools/testing/selftests/alsa/pcm-test.c +++ b/tools/testing/selftests/alsa/pcm-test.c @@ -15,12 +15,21 @@ #include <stdbool.h> #include <errno.h> #include <assert.h> +#include <pthread.h> #include "../kselftest.h" #include "alsa-local.h" typedef struct timespec timestamp_t; +struct card_data { + int card; + pthread_t thread; + struct card_data *next; +}; + +struct card_data *card_list = NULL; + struct pcm_data { snd_pcm_t *handle; int card; @@ -31,19 +40,19 @@ struct pcm_data { struct pcm_data *next; }; -int num_pcms = 0; struct pcm_data *pcm_list = NULL; int num_missing = 0; struct pcm_data *pcm_missing = NULL; -struct time_test_def { - const char *cfg_prefix; - const char *format; - long rate; - long channels; - long period_size; - long buffer_size; +snd_config_t *default_pcm_config; + +/* Lock while reporting results since kselftest doesn't */ +pthread_mutex_t results_lock = PTHREAD_MUTEX_INITIALIZER; + +enum test_class { + TEST_CLASS_DEFAULT, + TEST_CLASS_SYSTEM, }; void timestamp_now(timestamp_t *tstamp) @@ -146,6 +155,7 @@ static void find_pcms(void) snd_ctl_t *handle; snd_pcm_info_t *pcm_info; snd_config_t *config, *card_config, *pcm_config; + struct card_data *card_data; snd_pcm_info_alloca(&pcm_info); @@ -167,6 +177,13 @@ static void find_pcms(void) card_config = conf_by_card(card); + card_data = calloc(1, sizeof(*card_data)); + if (!card_data) + ksft_exit_fail_msg("Out of memory\n"); + card_data->card = card; + card_data->next = card_list; + card_list = card_data; + dev = -1; while (1) { if (snd_ctl_pcm_next_device(handle, &dev) < 0) @@ -209,7 +226,6 @@ static void find_pcms(void) pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL); pcm_data->next = pcm_list; pcm_list = pcm_data; - num_pcms++; } } } @@ -228,45 +244,64 @@ static void find_pcms(void) snd_config_delete(config); } -static void test_pcm_time1(struct pcm_data *data, - const struct time_test_def *test) +static void test_pcm_time(struct pcm_data *data, enum test_class class, + const char *test_name, snd_config_t *pcm_cfg) { char name[64], key[128], msg[256]; const char *cs; int i, err; snd_pcm_t *handle = NULL; snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED; - snd_pcm_format_t format; + snd_pcm_format_t format, old_format; + const char *alt_formats[8]; unsigned char *samples = NULL; snd_pcm_sframes_t frames; long long ms; long rate, channels, period_size, buffer_size; - unsigned int rchannels; unsigned int rrate; snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold; timestamp_t tstamp; - bool pass = false, automatic = true; + bool pass = false; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; - bool skip = false; + const char *test_class_name; + bool skip = true; + const char *desc; + + switch (class) { + case TEST_CLASS_DEFAULT: + test_class_name = "default"; + break; + case TEST_CLASS_SYSTEM: + test_class_name = "system"; + break; + default: + ksft_exit_fail_msg("Unknown test class %d\n", class); + break; + } + + desc = conf_get_string(pcm_cfg, "description", NULL, NULL); + if (desc) + ksft_print_msg("%s.%s.%d.%d.%d.%s - %s\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + desc); + snd_pcm_hw_params_alloca(&hw_params); snd_pcm_sw_params_alloca(&sw_params); - cs = conf_get_string(data->pcm_config, test->cfg_prefix, "format", test->format); + cs = conf_get_string(pcm_cfg, "format", NULL, "S16_LE"); format = snd_pcm_format_value(cs); if (format == SND_PCM_FORMAT_UNKNOWN) ksft_exit_fail_msg("Wrong format '%s'\n", cs); - rate = conf_get_long(data->pcm_config, test->cfg_prefix, "rate", test->rate); - channels = conf_get_long(data->pcm_config, test->cfg_prefix, "channels", test->channels); - period_size = conf_get_long(data->pcm_config, test->cfg_prefix, "period_size", test->period_size); - buffer_size = conf_get_long(data->pcm_config, test->cfg_prefix, "buffer_size", test->buffer_size); - - automatic = strcmp(test->format, snd_pcm_format_name(format)) == 0 && - test->rate == rate && - test->channels == channels && - test->period_size == period_size && - test->buffer_size == buffer_size; + conf_get_string_array(pcm_cfg, "alt_formats", NULL, + alt_formats, ARRAY_SIZE(alt_formats), NULL); + rate = conf_get_long(pcm_cfg, "rate", NULL, 48000); + channels = conf_get_long(pcm_cfg, "channels", NULL, 2); + period_size = conf_get_long(pcm_cfg, "period_size", NULL, 4096); + buffer_size = conf_get_long(pcm_cfg, "buffer_size", NULL, 16384); samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8); if (!samples) @@ -296,32 +331,39 @@ static void test_pcm_time1(struct pcm_data *data, snd_pcm_access_name(access), snd_strerror(err)); goto __close; } + i = -1; __format: err = snd_pcm_hw_params_set_format(handle, hw_params, format); if (err < 0) { - if (automatic && format == SND_PCM_FORMAT_S16_LE) { - format = SND_PCM_FORMAT_S32_LE; - ksft_print_msg("%s.%d.%d.%d.%s.%s format S16_LE -> S32_LE\n", - test->cfg_prefix, - data->card, data->device, data->subdevice, - snd_pcm_stream_name(data->stream), - snd_pcm_access_name(access)); + i++; + if (i < ARRAY_SIZE(alt_formats) && alt_formats[i]) { + old_format = format; + format = snd_pcm_format_value(alt_formats[i]); + if (format != SND_PCM_FORMAT_UNKNOWN) { + ksft_print_msg("%s.%d.%d.%d.%s.%s format %s -> %s\n", + test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + snd_pcm_access_name(access), + snd_pcm_format_name(old_format), + snd_pcm_format_name(format)); + samples = realloc(samples, (rate * channels * + snd_pcm_format_physical_width(format)) / 8); + if (!samples) + ksft_exit_fail_msg("Out of memory\n"); + snd_pcm_format_set_silence(format, samples, rate * channels); + goto __format; + } } snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s", snd_pcm_format_name(format), snd_strerror(err)); goto __close; } - rchannels = channels; - err = snd_pcm_hw_params_set_channels_near(handle, hw_params, &rchannels); + err = snd_pcm_hw_params_set_channels(handle, hw_params, channels); if (err < 0) { snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err)); goto __close; } - if (rchannels != channels) { - snprintf(msg, sizeof(msg), "channels unsupported %ld != %ld", channels, rchannels); - skip = true; - goto __close; - } rrate = rate; err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0); if (err < 0) { @@ -329,8 +371,7 @@ __format: goto __close; } if (rrate != rate) { - snprintf(msg, sizeof(msg), "rate unsupported %ld != %ld", rate, rrate); - skip = true; + snprintf(msg, sizeof(msg), "rate mismatch %ld != %ld", rate, rrate); goto __close; } rperiod_size = period_size; @@ -377,8 +418,8 @@ __format: goto __close; } - ksft_print_msg("%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n", - test->cfg_prefix, + ksft_print_msg("%s.%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n", + test_class_name, test_name, data->card, data->device, data->subdevice, snd_pcm_stream_name(data->stream), snd_pcm_access_name(access), @@ -387,6 +428,9 @@ __format: (long)rperiod_size, (long)rbuffer_size, (long)start_threshold); + /* Set all the params, actually run the test */ + skip = false; + timestamp_now(&tstamp); for (i = 0; i < 4; i++) { if (data->stream == SND_PCM_STREAM_PLAYBACK) { @@ -426,48 +470,120 @@ __format: msg[0] = '\0'; pass = true; __close: - if (!skip) { - ksft_test_result(pass, "%s.%d.%d.%d.%s%s%s\n", - test->cfg_prefix, + pthread_mutex_lock(&results_lock); + + switch (class) { + case TEST_CLASS_SYSTEM: + test_class_name = "system"; + /* + * Anything specified as specific to this system + * should always be supported. + */ + ksft_test_result(!skip, "%s.%s.%d.%d.%d.%s.params\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream)); + break; + default: + break; + } + + if (!skip) + ksft_test_result(pass, "%s.%s.%d.%d.%d.%s%s%s\n", + test_class_name, test_name, data->card, data->device, data->subdevice, snd_pcm_stream_name(data->stream), msg[0] ? " " : "", msg); - } else { - ksft_test_result_skip("%s.%d.%d.%d.%s%s%s\n", - test->cfg_prefix, - data->card, data->device, - data->subdevice, - snd_pcm_stream_name(data->stream), - msg[0] ? " " : "", msg); - } + else + ksft_test_result_skip("%s.%s.%d.%d.%d.%s%s%s\n", + test_class_name, test_name, + data->card, data->device, data->subdevice, + snd_pcm_stream_name(data->stream), + msg[0] ? " " : "", msg); + + pthread_mutex_unlock(&results_lock); + free(samples); if (handle) snd_pcm_close(handle); } -static const struct time_test_def time_tests[] = { - /* name format rate chan period buffer */ - { "8k.1.big", "S16_LE", 8000, 2, 8000, 32000 }, - { "8k.2.big", "S16_LE", 8000, 2, 8000, 32000 }, - { "44k1.2.big", "S16_LE", 44100, 2, 22050, 192000 }, - { "48k.2.small", "S16_LE", 48000, 2, 512, 4096 }, - { "48k.2.big", "S16_LE", 48000, 2, 24000, 192000 }, - { "48k.6.big", "S16_LE", 48000, 6, 48000, 576000 }, - { "96k.2.big", "S16_LE", 96000, 2, 48000, 192000 }, -}; +void run_time_tests(struct pcm_data *pcm, enum test_class class, + snd_config_t *cfg) +{ + const char *test_name, *test_type; + snd_config_t *pcm_cfg; + snd_config_iterator_t i, next; + + if (!cfg) + return; + + cfg = conf_get_subtree(cfg, "test", NULL); + if (cfg == NULL) + return; + + snd_config_for_each(i, next, cfg) { + pcm_cfg = snd_config_iterator_entry(i); + if (snd_config_get_id(pcm_cfg, &test_name) < 0) + ksft_exit_fail_msg("snd_config_get_id\n"); + test_type = conf_get_string(pcm_cfg, "type", NULL, "time"); + if (strcmp(test_type, "time") == 0) + test_pcm_time(pcm, class, test_name, pcm_cfg); + else + ksft_exit_fail_msg("unknown test type '%s'\n", test_type); + } +} + +void *card_thread(void *data) +{ + struct card_data *card = data; + struct pcm_data *pcm; + + for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { + if (pcm->card != card->card) + continue; + + run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config); + run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config); + } + + return 0; +} int main(void) { + struct card_data *card; struct pcm_data *pcm; - int i; + snd_config_t *global_config, *cfg, *pcm_cfg; + int num_pcm_tests = 0, num_tests, num_std_pcm_tests; + int ret; + void *thread_ret; ksft_print_header(); + global_config = conf_load_from_file("pcm-test.conf"); + default_pcm_config = conf_get_subtree(global_config, "pcm", NULL); + if (default_pcm_config == NULL) + ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n"); + conf_load(); find_pcms(); - ksft_set_plan(num_missing + num_pcms * ARRAY_SIZE(time_tests)); + num_std_pcm_tests = conf_get_count(default_pcm_config, "test", NULL); + + for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { + num_pcm_tests += num_std_pcm_tests; + cfg = pcm->pcm_config; + if (cfg == NULL) + continue; + /* Setting params is reported as a separate test */ + num_tests = conf_get_count(cfg, "test", NULL) * 2; + if (num_tests > 0) + num_pcm_tests += num_tests; + } + + ksft_set_plan(num_missing + num_pcm_tests); for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) { ksft_test_result(false, "test.missing.%d.%d.%d.%s\n", @@ -475,12 +591,25 @@ int main(void) snd_pcm_stream_name(pcm->stream)); } - for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { - for (i = 0; i < ARRAY_SIZE(time_tests); i++) { - test_pcm_time1(pcm, &time_tests[i]); + for (card = card_list; card != NULL; card = card->next) { + ret = pthread_create(&card->thread, NULL, card_thread, card); + if (ret != 0) { + ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n", + card->card, ret, + strerror(errno)); + } + } + + for (card = card_list; card != NULL; card = card->next) { + ret = pthread_join(card->thread, &thread_ret); + if (ret != 0) { + ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n", + card->card, ret, + strerror(errno)); } } + snd_config_delete(global_config); conf_free(); ksft_exit_pass(); diff --git a/tools/testing/selftests/alsa/pcm-test.conf b/tools/testing/selftests/alsa/pcm-test.conf new file mode 100644 index 000000000000..71bd3f78a6f2 --- /dev/null +++ b/tools/testing/selftests/alsa/pcm-test.conf @@ -0,0 +1,63 @@ +pcm.test.time1 { + description "8kHz mono large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 8000 + channels 1 + period_size 8000 + buffer_size 32000 +} +pcm.test.time2 { + description "8kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 8000 + channels 2 + period_size 8000 + buffer_size 32000 +} +pcm.test.time3 { + description "44.1kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 44100 + channels 2 + period_size 22500 + buffer_size 192000 +} +pcm.test.time4 { + description "48kHz stereo small periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 512 + buffer_size 4096 +} +pcm.test.time5 { + description "48kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 24000 + buffer_size 192000 +} +pcm.test.time6 { + description "48kHz 6 channel large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 48000 + channels 2 + period_size 48000 + buffer_size 576000 +} +pcm.test.time7 { + description "96kHz stereo large periods" + format S16_LE + alt_formats [ S32_LE ] + rate 96000 + channels 2 + period_size 48000 + buffer_size 192000 +} |