diff options
author | Mark Brown <broonie@kernel.org> | 2019-11-22 20:56:02 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2019-11-22 20:56:02 +0100 |
commit | 8c4d2a0bfbd27d030e4652b714cd5a1598f3559b (patch) | |
tree | d8ea72d906a690109d2f7aeeb2dc9ec529c0d5a6 | |
parent | Merge branch 'asoc-5.4' into asoc-linus (diff) | |
parent | ASoC: DMI long name - avoid to add board name if matches with product name (diff) | |
download | linux-8c4d2a0bfbd27d030e4652b714cd5a1598f3559b.tar.xz linux-8c4d2a0bfbd27d030e4652b714cd5a1598f3559b.zip |
Merge branch 'asoc-5.5' into asoc-next
228 files changed, 12215 insertions, 3527 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml new file mode 100644 index 000000000000..75e0cbe6be70 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/adi,adau7118.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + + +title: Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter + +maintainers: + - Nuno Sá <nuno.sa@analog.com> + +description: | + Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter over I2C or HW + standalone mode. + https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU7118.pdf + +properties: + compatible: + enum: + - adi,adau7118 + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + iovdd-supply: + description: Digital Input/Output Power Supply. + + dvdd-supply: + description: Internal Core Digital Power Supply. + + adi,decimation-ratio: + description: | + This property set's the decimation ratio of PDM to PCM audio data. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [64, 32, 16] + default: 64 + + adi,pdm-clk-map: + description: | + The ADAU7118 has two PDM clocks for the four Inputs. Each input must be + assigned to one of these two clocks. This property set's the mapping + between the clocks and the inputs. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + - minItems: 4 + maxItems: 4 + items: + maximum: 1 + default: [0, 0, 1, 1] + +required: + - "#sound-dai-cells" + - compatible + - iovdd-supply + - dvdd-supply + +examples: + - | + i2c { + /* example with i2c support */ + #address-cells = <1>; + #size-cells = <0>; + adau7118_codec: audio-codec@14 { + compatible = "adi,adau7118"; + reg = <0x14>; + #sound-dai-cells = <0>; + iovdd-supply = <&supply>; + dvdd-supply = <&supply>; + adi,pdm-clk-map = <1 1 0 0>; + adi,decimation-ratio = <16>; + }; + }; + + /* example with hw standalone mode */ + adau7118_codec_hw: adau7118-codec-hw { + compatible = "adi,adau7118"; + #sound-dai-cells = <0>; + iovdd-supply = <&supply>; + dvdd-supply = <&supply>; + }; diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml new file mode 100644 index 000000000000..b8f89c7258eb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml @@ -0,0 +1,267 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/allwinner,sun4i-a10-codec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A10 Codec Device Tree Bindings + +maintainers: + - Chen-Yu Tsai <wens@csie.org> + - Maxime Ripard <maxime.ripard@bootlin.com> + +properties: + "#sound-dai-cells": + const: 0 + + compatible: + enum: + - allwinner,sun4i-a10-codec + - allwinner,sun6i-a31-codec + - allwinner,sun7i-a20-codec + - allwinner,sun8i-a23-codec + - allwinner,sun8i-h3-codec + - allwinner,sun8i-v3s-codec + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Bus Clock + - description: Module Clock + + clock-names: + items: + - const: apb + - const: codec + + dmas: + items: + - description: RX DMA Channel + - description: TX DMA Channel + + dma-names: + items: + - const: rx + - const: tx + + resets: + maxItems: 1 + + allwinner,audio-routing: + 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. + allOf: + - $ref: /schemas/types.yaml#definitions/non-unique-string-array + - minItems: 2 + maxItems: 18 + items: + enum: + # Audio Pins on the SoC + - HP + - HPCOM + - LINEIN + - LINEOUT + - MIC1 + - MIC2 + - MIC3 + + # Microphone Biases from the SoC + - HBIAS + - MBIAS + + # Board Connectors + - Headphone + - Headset Mic + - Line In + - Line Out + - Mic + - Speaker + + allwinner,codec-analog-controls: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the codec analog controls in the PRCM + + allwinner,pa-gpios: + description: GPIO to enable the external amplifier + +required: + - "#sound-dai-cells" + - compatible + - reg + - interrupts + - clocks + - clock-names + - dmas + - dma-names + +allOf: + - if: + properties: + compatible: + enum: + - allwinner,sun6i-a31-codec + - allwinner,sun8i-a23-codec + - allwinner,sun8i-h3-codec + - allwinner,sun8i-v3s-codec + + then: + if: + properties: + compatible: + const: allwinner,sun6i-a31-codec + + then: + required: + - resets + - allwinner,audio-routing + + else: + required: + - resets + - allwinner,audio-routing + - allwinner,codec-analog-controls + + - if: + properties: + compatible: + enum: + - allwinner,sun6i-a31-codec + + then: + properties: + allwinner,audio-routing: + items: + enum: + - HP + - HPCOM + - LINEIN + - LINEOUT + - MIC1 + - MIC2 + - MIC3 + - HBIAS + - MBIAS + - Headphone + - Headset Mic + - Line In + - Line Out + - Mic + - Speaker + + - if: + properties: + compatible: + enum: + - allwinner,sun8i-a23-codec + + then: + properties: + allwinner,audio-routing: + items: + enum: + - HP + - HPCOM + - LINEIN + - MIC1 + - MIC2 + - HBIAS + - MBIAS + - Headphone + - Headset Mic + - Line In + - Line Out + - Mic + - Speaker + + - if: + properties: + compatible: + enum: + - allwinner,sun8i-h3-codec + + then: + properties: + allwinner,audio-routing: + items: + enum: + - HP + - HPCOM + - LINEIN + - LINEOUT + - MIC1 + - MIC2 + - HBIAS + - MBIAS + - Headphone + - Headset Mic + - Line In + - Line Out + - Mic + - Speaker + + - if: + properties: + compatible: + enum: + - allwinner,sun8i-v3s-codec + + then: + properties: + allwinner,audio-routing: + items: + enum: + - HP + - HPCOM + - MIC1 + - HBIAS + - Headphone + - Headset Mic + - Line In + - Line Out + - Mic + - Speaker + +additionalProperties: false + +examples: + - | + codec@1c22c00 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun7i-a20-codec"; + reg = <0x01c22c00 0x40>; + interrupts = <0 30 4>; + clocks = <&apb0_gates 0>, <&codec_clk>; + clock-names = "apb", "codec"; + dmas = <&dma 0 19>, <&dma 0 19>; + dma-names = "rx", "tx"; + }; + + - | + codec@1c22c00 { + #sound-dai-cells = <0>; + compatible = "allwinner,sun6i-a31-codec"; + reg = <0x01c22c00 0x98>; + interrupts = <0 29 4>; + clocks = <&ccu 61>, <&ccu 135>; + clock-names = "apb", "codec"; + resets = <&ccu 42>; + dmas = <&dma 15>, <&dma 15>; + dma-names = "rx", "tx"; + allwinner,audio-routing = + "Headphone", "HP", + "Speaker", "LINEOUT", + "LINEIN", "Line In", + "MIC1", "MBIAS", + "MIC1", "Mic", + "MIC2", "HBIAS", + "MIC2", "Headset Mic"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml new file mode 100644 index 000000000000..85305b4c2729 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/allwinner,sun8i-a23-codec-analog.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A23 Analog Codec Device Tree Bindings + +maintainers: + - Chen-Yu Tsai <wens@csie.org> + - Maxime Ripard <maxime.ripard@bootlin.com> + +properties: + compatible: + enum: + # FIXME: This is documented in the PRCM binding, but needs to be + # migrated here at some point + # - allwinner,sun8i-a23-codec-analog + - allwinner,sun8i-h3-codec-analog + - allwinner,sun8i-v3s-codec-analog + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + codec_analog: codec-analog@1f015c0 { + compatible = "allwinner,sun8i-h3-codec-analog"; + reg = <0x01f015c0 0x4>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt index 0e76946385ae..17530120ccfc 100644 --- a/Documentation/devicetree/bindings/sound/arndale.txt +++ b/Documentation/devicetree/bindings/sound/arndale.txt @@ -1,8 +1,9 @@ Audio Binding for Arndale boards Required properties: -- compatible : Can be the following, - "samsung,arndale-rt5631" +- compatible : Can be one of the following: + "samsung,arndale-rt5631", + "samsung,arndale-wm1811" - samsung,audio-cpu: The phandle of the Samsung I2S controller - samsung,audio-codec: The phandle of the audio codec diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.txt b/Documentation/devicetree/bindings/sound/fsl,mqs.txt new file mode 100644 index 000000000000..40353fc30255 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,mqs.txt @@ -0,0 +1,36 @@ +fsl,mqs audio CODEC + +Required properties: + - compatible : Must contain one of "fsl,imx6sx-mqs", "fsl,codec-mqs" + "fsl,imx8qm-mqs", "fsl,imx8qxp-mqs". + - clocks : A list of phandles + clock-specifiers, one for each entry in + clock-names + - clock-names : "mclk" - must required. + "core" - required if compatible is "fsl,imx8qm-mqs", it + is for register access. + - gpr : A phandle of General Purpose Registers in IOMUX Controller. + Required if compatible is "fsl,imx6sx-mqs". + +Required if compatible is "fsl,imx8qm-mqs": + - power-domains: A phandle of PM domain provider node. + - reg: Offset and length of the register set for the device. + +Example: + +mqs: mqs { + compatible = "fsl,imx6sx-mqs"; + gpr = <&gpr>; + clocks = <&clks IMX6SX_CLK_SAI1>; + clock-names = "mclk"; + status = "disabled"; +}; + +mqs: mqs@59850000 { + compatible = "fsl,imx8qm-mqs"; + reg = <0x59850000 0x10000>; + clocks = <&clk IMX8QM_AUD_MQS_IPG>, + <&clk IMX8QM_AUD_MQS_HMCLK>; + clock-names = "core", "mclk"; + power-domains = <&pd_mqs0>; + status = "disabled"; +}; diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt index 1084f7f22eea..8ca52dcc5572 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt @@ -1,4 +1,4 @@ -* Audio codec controlled by ChromeOS EC +Audio codec controlled by ChromeOS EC Google's ChromeOS EC codec is a digital mic codec provided by the Embedded Controller (EC) and is controlled via a host-command interface. @@ -9,10 +9,27 @@ Documentation/devicetree/bindings/mfd/cros-ec.txt). Required properties: - compatible: Must contain "google,cros-ec-codec" - #sound-dai-cells: Should be 1. The cell specifies number of DAIs. -- max-dmic-gain: A number for maximum gain in dB on digital microphone. + +Optional properties: +- reg: Pysical base address and length of shared memory region from EC. + It contains 3 unsigned 32-bit integer. The first 2 integers + combine to become an unsigned 64-bit physical address. The last + one integer is length of the shared memory. +- memory-region: Shared memory region to EC. A "shared-dma-pool". See + ../reserved-memory/reserved-memory.txt for details. Example: +{ + ... + + reserved_mem: reserved_mem { + compatible = "shared-dma-pool"; + reg = <0 0x52800000 0 0x100000>; + no-map; + }; +} + cros-ec@0 { compatible = "google,cros-ec-spi"; @@ -21,6 +38,7 @@ cros-ec@0 { cros_ec_codec: ec-codec { compatible = "google,cros-ec-codec"; #sound-dai-cells = <1>; - max-dmic-gain = <43>; + reg = <0x0 0x10500000 0x80000>; + memory-region = <&reserved_mem>; }; }; diff --git a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt index 396ba38619f6..1f1cba4152ce 100644 --- a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt +++ b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt @@ -4,6 +4,10 @@ Required properties: - compatible = "mediatek,mt68183-audio"; - reg: register location and size - interrupts: should contain AFE interrupt +- resets: Must contain an entry for each entry in reset-names + See ../reset/reset.txt for details. +- reset-names: should have these reset names: + "audiosys"; - power-domains: should define the power domain - clocks: Must contain an entry for each entry in clock-names - clock-names: should have these clock names: @@ -20,6 +24,8 @@ Example: compatible = "mediatek,mt8183-audio"; reg = <0 0x11220000 0 0x1000>; interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>; + resets = <&watchdog MT8183_TOPRGU_AUDIO_SW_RST>; + reset-names = "audiosys"; power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>; clocks = <&infrasys CLK_INFRA_AUDIO>, <&infrasys CLK_INFRA_AUDIO_26M_BCLK>, diff --git a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt index d6d5207fa996..decaa013a07e 100644 --- a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt +++ b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt @@ -2,14 +2,19 @@ MT8183 with MT6358, TS3A227 and MAX98357 CODECS Required properties: - compatible : "mediatek,mt8183_mt6358_ts3a227_max98357" -- mediatek,headset-codec: the phandles of ts3a227 codecs - mediatek,platform: the phandle of MT8183 ASoC platform +Optional properties: +- mediatek,headset-codec: the phandles of ts3a227 codecs +- mediatek,ec-codec: the phandle of EC codecs. + See google,cros-ec-codec.txt for more details. + Example: sound { compatible = "mediatek,mt8183_mt6358_ts3a227_max98357"; mediatek,headset-codec = <&ts3a227>; + mediatek,ec-codec = <&ec_codec>; mediatek,platform = <&afe>; }; diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt deleted file mode 100644 index 0cf0f819b823..000000000000 --- a/Documentation/devicetree/bindings/sound/renesas,fsi.txt +++ /dev/null @@ -1,31 +0,0 @@ -Renesas FSI - -Required properties: -- compatible : "renesas,fsi2-<soctype>", - "renesas,sh_fsi2" or "renesas,sh_fsi" as - fallback. - Examples with soctypes are: - - "renesas,fsi2-r8a7740" (R-Mobile A1) - - "renesas,fsi2-sh73a0" (SH-Mobile AG5) -- reg : Should contain the register physical address and length -- interrupts : Should contain FSI interrupt - -- fsia,spdif-connection : FSI is connected by S/PDIF -- fsia,stream-mode-support : FSI supports 16bit stream mode. -- fsia,use-internal-clock : FSI uses internal clock when master mode. - -- fsib,spdif-connection : same as fsia -- fsib,stream-mode-support : same as fsia -- fsib,use-internal-clock : same as fsia - -Example: - -sh_fsi2: sh_fsi2@ec230000 { - compatible = "renesas,sh_fsi2"; - reg = <0xec230000 0x400>; - interrupts = <0 146 0x4>; - - fsia,spdif-connection; - fsia,stream-mode-support; - fsia,use-internal-clock; -}; diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.yaml b/Documentation/devicetree/bindings/sound/renesas,fsi.yaml new file mode 100644 index 000000000000..140a37fc3c0b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/renesas,fsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas FSI Sound Driver Device Tree Bindings + +maintainers: + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + +properties: + $nodename: + pattern: "^sound@.*" + + compatible: + oneOf: + # for FSI2 SoC + - items: + - enum: + - renesas,fsi2-sh73a0 + - renesas,fsi2-r8a7740 + - enum: + - renesas,sh_fsi2 + # for Generic + - items: + - enum: + - renesas,sh_fsi + - renesas,sh_fsi2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + fsia,spdif-connection: + $ref: /schemas/types.yaml#/definitions/flag + description: FSI is connected by S/PDIF + + fsia,stream-mode-support: + $ref: /schemas/types.yaml#/definitions/flag + description: FSI supports 16bit stream mode + + fsia,use-internal-clock: + $ref: /schemas/types.yaml#/definitions/flag + description: FSI uses internal clock when master mode + + fsib,spdif-connection: + $ref: /schemas/types.yaml#/definitions/flag + description: same as fsia + + fsib,stream-mode-support: + $ref: /schemas/types.yaml#/definitions/flag + description: same as fsia + + fsib,use-internal-clock: + $ref: /schemas/types.yaml#/definitions/flag + description: same as fsia + +required: + - compatible + - reg + - interrupts + +examples: + - | + sh_fsi2: sound@ec230000 { + compatible = "renesas,fsi2-r8a7740", "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupts = <0 146 0x4>; + + fsia,spdif-connection; + fsia,stream-mode-support; + fsia,use-internal-clock; + }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 5c52182f7dcf..797fd035434c 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -268,6 +268,7 @@ Required properties: - "renesas,rcar_sound-r8a7745" (RZ/G1E) - "renesas,rcar_sound-r8a77470" (RZ/G1C) - "renesas,rcar_sound-r8a774a1" (RZ/G2M) + - "renesas,rcar_sound-r8a774b1" (RZ/G2N) - "renesas,rcar_sound-r8a774c0" (RZ/G2E) - "renesas,rcar_sound-r8a7778" (R-Car M1A) - "renesas,rcar_sound-r8a7779" (R-Car H1) diff --git a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt index a805aa99ad75..e9c58b204399 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-max98090.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-max98090.txt @@ -5,15 +5,38 @@ Required properties: - rockchip,model: The user-visible name of this sound complex - rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's connected to the CODEC -- rockchip,audio-codec: The phandle of the MAX98090 audio codec -- rockchip,headset-codec: The phandle of Ext chip for jack detection + +Optional properties: +- rockchip,audio-codec: The phandle of the MAX98090 audio codec. +- rockchip,headset-codec: The phandle of Ext chip for jack detection. This is + required if there is rockchip,audio-codec. +- rockchip,hdmi-codec: The phandle of HDMI device for HDMI codec. Example: +/* For max98090-only board. */ +sound { + compatible = "rockchip,rockchip-audio-max98090"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,audio-codec = <&max98090>; + rockchip,headset-codec = <&headsetcodec>; +}; + +/* For HDMI-only board. */ +sound { + compatible = "rockchip,rockchip-audio-max98090"; + rockchip,model = "ROCKCHIP-I2S"; + rockchip,i2s-controller = <&i2s>; + rockchip,hdmi-codec = <&hdmi>; +}; + +/* For max98090 plus HDMI board. */ sound { compatible = "rockchip,rockchip-audio-max98090"; rockchip,model = "ROCKCHIP-I2S"; rockchip,i2s-controller = <&i2s>; rockchip,audio-codec = <&max98090>; rockchip,headset-codec = <&headsetcodec>; + rockchip,hdmi-codec = <&hdmi>; }; diff --git a/Documentation/devicetree/bindings/sound/rt1011.txt b/Documentation/devicetree/bindings/sound/rt1011.txt index 35a23e60d679..02d53b9aa247 100644 --- a/Documentation/devicetree/bindings/sound/rt1011.txt +++ b/Documentation/devicetree/bindings/sound/rt1011.txt @@ -20,6 +20,14 @@ Required properties: | 1 | 1 | 0x3b | ------------------------------------- +Optional properties: + +- realtek,temperature_calib + u32. The temperature was measured while doing the calibration. Units: Celsius degree + +- realtek,r0_calib + u32. This is r0 calibration data which was measured in factory mode. + Pins on the device (for linking into audio routes) for RT1011: * SPO @@ -29,4 +37,6 @@ Example: rt1011: codec@38 { compatible = "realtek,rt1011"; reg = <0x38>; + realtek,temperature_calib = <25>; + realtek,r0_calib = <0x224050>; }; diff --git a/Documentation/devicetree/bindings/sound/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt index 312e9a129530..30e927a28369 100644 --- a/Documentation/devicetree/bindings/sound/rt5682.txt +++ b/Documentation/devicetree/bindings/sound/rt5682.txt @@ -27,6 +27,11 @@ Optional properties: - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. +- realtek,btndet-delay + The debounce delay for push button. + The delay time is realtek,btndet-delay value multiple of 8.192 ms. + If absent, the default is 16. + Pins on the device (for linking into audio routes) for RT5682: * DMIC L1 @@ -47,4 +52,5 @@ rt5682 { realtek,dmic1-data-pin = <1>; realtek,dmic1-clk-pin = <1>; realtek,jd-src = <1>; + realtek,btndet-delay = <16>; }; diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.txt b/Documentation/devicetree/bindings/sound/samsung,odroid.txt deleted file mode 100644 index e9da2200e173..000000000000 --- a/Documentation/devicetree/bindings/sound/samsung,odroid.txt +++ /dev/null @@ -1,54 +0,0 @@ -Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec - -Required properties: - - - compatible - "hardkernel,odroid-xu3-audio" - for Odroid XU3 board, - "hardkernel,odroid-xu4-audio" - for Odroid XU4 board (deprecated), - "samsung,odroid-xu3-audio" - for Odroid XU3 board (deprecated), - "samsung,odroid-xu4-audio" - for Odroid XU4 board (deprecated) - - model - the user-visible name of this sound complex - - clocks - should contain entries matching clock names in the clock-names - property - - samsung,audio-widgets - this property specifies off-codec audio elements - like headphones or speakers, for details see widgets.txt - - samsung,audio-routing - a list of the connections between audio - components; each entry is a pair of strings, the first being the - connection's sink, the second being the connection's source; - valid names for sources and sinks are the MAX98090's pins (as - documented in its binding), and the jacks on the board - - For Odroid X2: - "Headphone Jack", "Mic Jack", "DMIC" - - For Odroid U3, XU3: - "Headphone Jack", "Speakers" - - For Odroid XU4: - no entries - -Required sub-nodes: - - - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S - controller - - 'codec' subnode with a 'sound-dai' property containing list of phandles - to the CODEC nodes, first entry must be corresponding to the MAX98090 - CODEC and the second entry must be the phandle of the HDMI IP block node - -Example: - -sound { - compatible = "hardkernel,odroid-xu3-audio"; - model = "Odroid-XU3"; - samsung,audio-routing = - "Headphone Jack", "HPL", - "Headphone Jack", "HPR", - "IN1", "Mic Jack", - "Mic Jack", "MICBIAS"; - - cpu { - sound-dai = <&i2s0 0>; - }; - codec { - sound-dai = <&hdmi>, <&max98090>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml new file mode 100644 index 000000000000..c6b244352d05 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung,odroid.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec + +maintainers: + - Krzysztof Kozlowski <krzk@kernel.org> + - Sylwester Nawrocki <s.nawrocki@samsung.com> + +properties: + compatible: + oneOf: + - const: hardkernel,odroid-xu3-audio + + - const: hardkernel,odroid-xu4-audio + deprecated: true + + - const: samsung,odroid-xu3-audio + deprecated: true + + - const: samsung,odroid-xu4-audio + deprecated: true + + model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + cpu: + type: object + properties: + sound-dai: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: phandles to the I2S controllers + + codec: + type: object + properties: + sound-dai: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles to the CODEC nodes, + first entry must be corresponding to the MAX98090 CODEC and + the second entry must be the phandle of the HDMI IP block node. + + samsung,audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: | + List of the connections between audio + components; each entry is a pair of strings, the first being the + connection's sink, the second being the connection's source; + valid names for sources and sinks are the MAX98090's pins (as + documented in its binding), and the jacks on the board. + For Odroid X2: "Headphone Jack", "Mic Jack", "DMIC" + For Odroid U3, XU3: "Headphone Jack", "Speakers" + For Odroid XU4: no entries + + samsung,audio-widgets: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: | + This property specifies off-codec audio elements + like headphones or speakers, for details see widgets.txt + +required: + - compatible + - model + - cpu + - codec + +examples: + - | + sound { + compatible = "hardkernel,odroid-xu3-audio"; + model = "Odroid-XU3"; + samsung,audio-routing = + "Headphone Jack", "HPL", + "Headphone Jack", "HPR", + "IN1", "Mic Jack", + "Mic Jack", "MICBIAS"; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&hdmi>, <&max98090>; + }; + }; + diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt deleted file mode 100644 index a88cb00fa096..000000000000 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ /dev/null @@ -1,84 +0,0 @@ -* Samsung I2S controller - -Required SoC Specific Properties: - -- compatible : should be one of the following. - - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with - secondary fifo, s/w reset control and internal mux for root clk src. - - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for - playback, stereo channel capture, secondary fifo using internal - or external dma, s/w reset control, internal mux for root clk src - and 7.1 channel TDM support for playback. TDM (Time division multiplexing) - is to allow transfer of multiple channel audio data on single data line. - - samsung,exynos7-i2s: with all the available features of exynos5 i2s, - exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo - with only external dma and more no.of root clk sampling frequencies. - - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports - stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with - slightly modified bit offsets. - -- reg: physical base address of the controller and length of memory mapped - region. -- dmas: list of DMA controller phandle and DMA request line ordered pairs. -- dma-names: identifier string for each DMA request line in the dmas property. - These strings correspond 1:1 with the ordered pairs in dmas. -- clocks: Handle to iis clock and RCLK source clk. -- clock-names: - i2s0 uses some base clocks from CMU and some are from audio subsystem internal - clock controller. The clock names for i2s0 should be "iis", "i2s_opclk0" and - "i2s_opclk1" as shown in the example below. - i2s1 and i2s2 uses clocks from CMU. The clock names for i2s1 and i2s2 should - be "iis" and "i2s_opclk0". - "iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root - clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2 - doesn't have any such mux. -- #clock-cells: should be 1, this property must be present if the I2S device - is a clock provider in terms of the common clock bindings, described in - ../clock/clock-bindings.txt. -- clock-output-names (deprecated): from the common clock bindings, names of - the CDCLK I2S output clocks, suggested values are "i2s_cdclk0", "i2s_cdclk1", - "i2s_cdclk3" for the I2S0, I2S1, I2S2 devices respectively. - -There are following clocks available at the I2S device nodes: - CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock, - CLK_I2S_RCLK_PSR - the RCLK prescaler divider clock (corresponding to the - IISPSR register), - CLK_I2S_RCLK_SRC - the RCLKSRC mux clock (corresponding to RCLKSRC bit in - IISMOD register). - -Refer to the SoC datasheet for availability of the above clocks. -The CLK_I2S_RCLK_PSR and CLK_I2S_RCLK_SRC clocks are usually only available -in the IIS Multi Audio Interface. - -Note: Old DTs may not have the #clock-cells property and then not use the I2S -node as a clock supplier. - -Optional SoC Specific Properties: - -- samsung,idma-addr: Internal DMA register base address of the audio - sub system(used in secondary sound source). -- pinctrl-0: Should specify pin control groups used for this controller. -- pinctrl-names: Should contain only one value - "default". -- #sound-dai-cells: should be 1. - - -Example: - -i2s0: i2s@3830000 { - compatible = "samsung,s5pv210-i2s"; - reg = <0x03830000 0x100>; - dmas = <&pdma0 10 - &pdma0 9 - &pdma0 8>; - dma-names = "tx", "rx", "tx-sec"; - clocks = <&clock_audss EXYNOS_I2S_BUS>, - <&clock_audss EXYNOS_I2S_BUS>, - <&clock_audss EXYNOS_SCLK_I2S>; - clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - #clock-cells = <1>; - samsung,idma-addr = <0x03000000>; - pinctrl-names = "default"; - pinctrl-0 = <&i2s0_bus>; - #sound-dai-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml new file mode 100644 index 000000000000..53e3bad4178c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/samsung-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SoC I2S controller + +maintainers: + - Krzysztof Kozlowski <krzk@kernel.org> + - Sylwester Nawrocki <s.nawrocki@samsung.com> + +properties: + compatible: + description: | + samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. + + samsung,s5pv210-i2s: for 8/16/24bit multichannel (5.1) I2S with + secondary FIFO, s/w reset control and internal mux for root clock + source. + + samsung,exynos5420-i2s: for 8/16/24bit multichannel (5.1) I2S for + playback, stereo channel capture, secondary FIFO using internal + or external DMA, s/w reset control, internal mux for root clock + source and 7.1 channel TDM support for playback; TDM (Time division + multiplexing) is to allow transfer of multiple channel audio data on + single data line. + + samsung,exynos7-i2s: with all the available features of Exynos5 I2S. + Exynos7 I2S has 7.1 channel TDM support for capture, secondary FIFO + with only external DMA and more number of root clock sampling + frequencies. + + samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports + stereo channels. Exynos7 I2S1 upgraded to 5.1 multichannel with + slightly modified bit offsets. + enum: + - samsung,s3c6410-i2s + - samsung,s5pv210-i2s + - samsung,exynos5420-i2s + - samsung,exynos7-i2s + - samsung,exynos7-i2s1 + + reg: + maxItems: 1 + + dmas: + minItems: 2 + maxItems: 3 + + dma-names: + oneOf: + - items: + - const: tx + - const: rx + - items: + - const: tx + - const: rx + - const: tx-sec + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + oneOf: + - items: + - const: iis + - items: # for I2S0 + - const: iis + - const: i2s_opclk0 + - const: i2s_opclk1 + - items: # for I2S1 and I2S2 + - const: iis + - const: i2s_opclk0 + description: | + "iis" is the I2S bus clock and i2s_opclk0, i2s_opclk1 are sources + of the root clock. I2S0 has internal mux to select the source + of root clock and I2S1 and I2S2 doesn't have any such mux. + + "#clock-cells": + const: 1 + + clock-output-names: + deprecated: true + oneOf: + - items: # for I2S0 + - const: i2s_cdclk0 + - items: # for I2S1 + - const: i2s_cdclk1 + - items: # for I2S2 + - const: i2s_cdclk2 + description: Names of the CDCLK I2S output clocks. + + samsung,idma-addr: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Internal DMA register base address of the audio + subsystem (used in secondary sound source). + + pinctrl-0: + description: Should specify pin control groups used for this controller. + + pinctrl-names: + const: default + + "#sound-dai-cells": + const: 1 + +required: + - compatible + - reg + - dmas + - dma-names + - clocks + - clock-names + +examples: + - | + #include <dt-bindings/clock/exynos-audss-clk.h> + + i2s0: i2s@3830000 { + compatible = "samsung,s5pv210-i2s"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10>, + <&pdma0 9>, + <&pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + clocks = <&clock_audss EXYNOS_I2S_BUS>, + <&clock_audss EXYNOS_I2S_BUS>, + <&clock_audss EXYNOS_SCLK_I2S>; + clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + #clock-cells = <1>; + samsung,idma-addr = <0x03000000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2s0_bus>; + #sound-dai-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt deleted file mode 100644 index 66579bbd3294..000000000000 --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt +++ /dev/null @@ -1,94 +0,0 @@ -* Allwinner A10 Codec - -Required properties: -- compatible: must be one of the following compatibles: - - "allwinner,sun4i-a10-codec" - - "allwinner,sun6i-a31-codec" - - "allwinner,sun7i-a20-codec" - - "allwinner,sun8i-a23-codec" - - "allwinner,sun8i-h3-codec" - - "allwinner,sun8i-v3s-codec" -- reg: must contain the registers location and length -- interrupts: must contain the codec interrupt -- dmas: DMA channels for tx and rx dma. See the DMA client binding, - Documentation/devicetree/bindings/dma/dma.txt -- dma-names: should include "tx" and "rx". -- clocks: a list of phandle + clock-specifer pairs, one for each entry - in clock-names. -- clock-names: should contain the following: - - "apb": the parent APB clock for this controller - - "codec": the parent module clock - -Optional properties: -- allwinner,pa-gpios: gpio to enable external amplifier - -Required properties for the following compatibles: - - "allwinner,sun6i-a31-codec" - - "allwinner,sun8i-a23-codec" - - "allwinner,sun8i-h3-codec" - - "allwinner,sun8i-v3s-codec" -- resets: phandle to the reset control for this device -- allwinner,audio-routing: A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names include: - - Audio pins on the SoC: - "HP" - "HPCOM" - "LINEIN" (not on sun8i-v3s) - "LINEOUT" (not on sun8i-a23 or sun8i-v3s) - "MIC1" - "MIC2" (not on sun8i-v3s) - "MIC3" (sun6i-a31 only) - - Microphone biases from the SoC: - "HBIAS" - "MBIAS" (not on sun8i-v3s) - - Board connectors: - "Headphone" - "Headset Mic" - "Line In" - "Line Out" - "Mic" - "Speaker" - -Required properties for the following compatibles: - - "allwinner,sun8i-a23-codec" - - "allwinner,sun8i-h3-codec" - - "allwinner,sun8i-v3s-codec" -- allwinner,codec-analog-controls: A phandle to the codec analog controls - block in the PRCM. - -Example: -codec: codec@1c22c00 { - #sound-dai-cells = <0>; - compatible = "allwinner,sun7i-a20-codec"; - reg = <0x01c22c00 0x40>; - interrupts = <0 30 4>; - clocks = <&apb0_gates 0>, <&codec_clk>; - clock-names = "apb", "codec"; - dmas = <&dma 0 19>, <&dma 0 19>; - dma-names = "rx", "tx"; -}; - -codec: codec@1c22c00 { - #sound-dai-cells = <0>; - compatible = "allwinner,sun6i-a31-codec"; - reg = <0x01c22c00 0x98>; - interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>; - clock-names = "apb", "codec"; - resets = <&ccu RST_APB1_CODEC>; - dmas = <&dma 15>, <&dma 15>; - dma-names = "rx", "tx"; - allwinner,audio-routing = - "Headphone", "HP", - "Speaker", "LINEOUT", - "LINEIN", "Line In", - "MIC1", "MBIAS", - "MIC1", "Mic", - "MIC2", "HBIAS", - "MIC2", "Headset Mic"; -}; diff --git a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt deleted file mode 100644 index 07356758bd91..000000000000 --- a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt +++ /dev/null @@ -1,17 +0,0 @@ -* Allwinner Codec Analog Controls - -Required properties: -- compatible: must be one of the following compatibles: - - "allwinner,sun8i-a23-codec-analog" - - "allwinner,sun8i-h3-codec-analog" - - "allwinner,sun8i-v3s-codec-analog" - -Required properties if not a sub-node of the PRCM node: -- reg: must contain the registers location and length - -Example: -prcm: prcm@1f01400 { - codec_analog: codec-analog { - compatible = "allwinner,sun8i-a23-codec-analog"; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/tas2562.txt b/Documentation/devicetree/bindings/sound/tas2562.txt new file mode 100644 index 000000000000..658e1fb18a99 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas2562.txt @@ -0,0 +1,34 @@ +Texas Instruments TAS2562 Smart PA + +The TAS2562 is a mono, digital input Class-D audio amplifier optimized for +efficiently driving high peak power into small loudspeakers. +Integrated speaker voltage and current sense provides for +real time monitoring of loudspeaker behavior. + +Required properties: + - #address-cells - Should be <1>. + - #size-cells - Should be <0>. + - compatible: - Should contain "ti,tas2562". + - reg: - The i2c address. Should be 0x4c, 0x4d, 0x4e or 0x4f. + - ti,imon-slot-no:- TDM TX current sense time slot. + +Optional properties: +- interrupt-parent: phandle to the interrupt controller which provides + the interrupt. +- interrupts: (GPIO) interrupt to which the chip is connected. +- shut-down: GPIO used to control the state of the device. + +Examples: +tas2562@4c { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,tas2562"; + reg = <0x4c>; + + interrupt-parent = <&gpio1>; + interrupts = <14>; + + shut-down = <&gpio1 15 0>; + ti,imon-slot-no = <0>; +}; + diff --git a/Documentation/devicetree/bindings/sound/tas2770.txt b/Documentation/devicetree/bindings/sound/tas2770.txt new file mode 100644 index 000000000000..ede6bb3d9637 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas2770.txt @@ -0,0 +1,37 @@ +Texas Instruments TAS2770 Smart PA + +The TAS2770 is a mono, digital input Class-D audio amplifier optimized for +efficiently driving high peak power into small loudspeakers. +Integrated speaker voltage and current sense provides for +real time monitoring of loudspeaker behavior. + +Required properties: + + - compatible: - Should contain "ti,tas2770". + - reg: - The i2c address. Should contain <0x4c>, <0x4d>,<0x4e>, or <0x4f>. + - #address-cells - Should be <1>. + - #size-cells - Should be <0>. + - ti,asi-format: - Sets TDM RX capture edge. 0->Rising; 1->Falling. + - ti,imon-slot-no:- TDM TX current sense time slot. + - ti,vmon-slot-no:- TDM TX voltage sense time slot. + +Optional properties: + +- interrupt-parent: the phandle to the interrupt controller which provides + the interrupt. +- interrupts: interrupt specification for data-ready. + +Examples: + + tas2770@4c { + compatible = "ti,tas2770"; + reg = <0x4c>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&msm_gpio>; + interrupts = <97 0>; + ti,asi-format = <0>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; + }; + diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt index 5d9cb84c661d..a02ecaab5183 100644 --- a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt +++ b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt @@ -25,6 +25,13 @@ Required properties: 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 { @@ -34,6 +41,7 @@ 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>; diff --git a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt index 5b3c33bb99e5..e372303697dc 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic31xx.txt @@ -29,6 +29,11 @@ Optional properties: 3 or MICBIAS_AVDD - MICBIAS output is connected to AVDD If this node is not mentioned or if the value is unknown, then micbias is set to 2.0V. +- ai31xx-ocmv - output common-mode voltage setting + 0 - 1.35V, + 1 - 1.5V, + 2 - 1.65V, + 3 - 1.8V Deprecated properties: diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 967e78c5ec0a..67b89db54aa2 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -16,7 +16,7 @@ properties: {} patternProperties: # Prefixes which are not vendors, but followed the pattern # DO NOT ADD NEW PROPERTIES TO THIS LIST - "^(at25|devbus|dmacap|dsa|exynos|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true + "^(at25|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true "^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true "^(pinctrl-single|#pinctrl-single|PowerPC),.*": true "^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true diff --git a/MAINTAINERS b/MAINTAINERS index e4f170d8bc29..236bb3630ec2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1002,6 +1002,7 @@ F: drivers/media/i2c/adv7842* ANALOG DEVICES INC ASOC CODEC DRIVERS M: Lars-Peter Clausen <lars@metafoo.de> +M: Nuno Sá <nuno.sa@analog.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://wiki.analog.com/ W: http://ez.analog.com/community/linux-device-drivers diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 1d15cf9b6821..6c2c44d0bdee 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -151,11 +151,22 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, return -EINVAL; } +static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct dw_hdmi_i2s_audio_data *audio = data; + struct dw_hdmi *hdmi = audio->hdmi; + + return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev); +} + static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, .get_eld = dw_hdmi_i2s_get_eld, .get_dai_id = dw_hdmi_i2s_get_dai_id, + .hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb, }; static int snd_dw_hdmi_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 521d689413c8..2102872bf43c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -191,6 +191,10 @@ struct dw_hdmi { struct mutex cec_notifier_mutex; struct cec_notifier *cec_notifier; + + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; + enum drm_connector_status last_connector_result; }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -215,6 +219,28 @@ static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset) return val; } +static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged) +{ + if (hdmi->plugged_cb && hdmi->codec_dev) + hdmi->plugged_cb(hdmi->codec_dev, plugged); +} + +int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + bool plugged; + + mutex_lock(&hdmi->mutex); + hdmi->plugged_cb = fn; + hdmi->codec_dev = codec_dev; + plugged = hdmi->last_connector_result == connector_status_connected; + handle_plugged_change(hdmi, plugged); + mutex_unlock(&hdmi->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb); + static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg) { regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data); @@ -2161,6 +2187,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); + enum drm_connector_status result; mutex_lock(&hdmi->mutex); hdmi->force = DRM_FORCE_UNSPECIFIED; @@ -2168,7 +2195,18 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) dw_hdmi_update_phy_mask(hdmi); mutex_unlock(&hdmi->mutex); - return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + + mutex_lock(&hdmi->mutex); + if (result != hdmi->last_connector_result) { + dev_dbg(hdmi->dev, "read_hpd result: %d", result); + handle_plugged_change(hdmi, + result == connector_status_connected); + hdmi->last_connector_result = result; + } + mutex_unlock(&hdmi->mutex); + + return result; } static int dw_hdmi_connector_get_modes(struct drm_connector *connector) @@ -2619,6 +2657,7 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->rxsense = true; hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); hdmi->mc_clkdis = 0x7f; + hdmi->last_connector_result = connector_status_disconnected; mutex_init(&hdmi->mutex); mutex_init(&hdmi->audio_mutex); diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 6f80ff4532ae..5af1d66d9eca 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -98,7 +98,10 @@ TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \ TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \ TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \ - TRACE_SYMBOL(EC_CMD_CODEC_I2S), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \ + TRACE_SYMBOL(EC_CMD_EC_CODEC_WOV), \ TRACE_SYMBOL(EC_CMD_REBOOT_EC), \ TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \ TRACE_SYMBOL(EC_CMD_ACPI_READ), \ diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index cf528c289857..9a0c8381a069 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -6,6 +6,8 @@ #ifndef __DW_HDMI__ #define __DW_HDMI__ +#include <sound/hdmi-codec.h> + struct drm_connector; struct drm_display_mode; struct drm_encoder; @@ -154,6 +156,8 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi); void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); +int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn, + struct device *codec_dev); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt); void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca); diff --git a/include/dt-bindings/sound/samsung-i2s.h b/include/dt-bindings/sound/samsung-i2s.h index 77545f14c379..250de0d6c734 100644 --- a/include/dt-bindings/sound/samsung-i2s.h +++ b/include/dt-bindings/sound/samsung-i2s.h @@ -2,8 +2,14 @@ #ifndef _DT_BINDINGS_SAMSUNG_I2S_H #define _DT_BINDINGS_SAMSUNG_I2S_H -#define CLK_I2S_CDCLK 0 -#define CLK_I2S_RCLK_SRC 1 -#define CLK_I2S_RCLK_PSR 2 +#define CLK_I2S_CDCLK 0 /* the CDCLK (CODECLKO) gate clock */ + +#define CLK_I2S_RCLK_SRC 1 /* the RCLKSRC mux clock (corresponding to + * RCLKSRC bit in IISMOD register) + */ + +#define CLK_I2S_RCLK_PSR 2 /* the RCLK prescaler divider clock + * (corresponding to the IISPSR register) + */ #endif /* _DT_BINDINGS_SAMSUNG_I2S_H */ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 98415686cbfa..69210881ebac 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -556,6 +556,9 @@ enum host_event_code { /* Keyboard recovery combo with hardware reinitialization */ EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30, + /* WoV */ + EC_HOST_EVENT_WOV = 31, + /* * The high bit of the event mask is not used as a host event code. If * it reads back as set, then the entire event mask should be @@ -1277,8 +1280,6 @@ enum ec_feature_code { * MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37, - /* EC supports audio codec. */ - EC_FEATURE_AUDIO_CODEC = 38, /* The MCU is a System Companion Processor (SCP). */ EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */ @@ -4468,92 +4469,246 @@ enum mkbp_cec_event { /*****************************************************************************/ -/* Commands for I2S recording on audio codec. */ +/* Commands for audio codec. */ +#define EC_CMD_EC_CODEC 0x00BC -#define EC_CMD_CODEC_I2S 0x00BC -#define EC_WOV_I2S_SAMPLE_RATE 48000 +enum ec_codec_subcmd { + EC_CODEC_GET_CAPABILITIES = 0x0, + EC_CODEC_GET_SHM_ADDR = 0x1, + EC_CODEC_SET_SHM_ADDR = 0x2, + EC_CODEC_SUBCMD_COUNT, +}; -enum ec_codec_i2s_subcmd { - EC_CODEC_SET_SAMPLE_DEPTH = 0x0, - EC_CODEC_SET_GAIN = 0x1, - EC_CODEC_GET_GAIN = 0x2, - EC_CODEC_I2S_ENABLE = 0x3, - EC_CODEC_I2S_SET_CONFIG = 0x4, - EC_CODEC_I2S_SET_TDM_CONFIG = 0x5, - EC_CODEC_I2S_SET_BCLK = 0x6, - EC_CODEC_I2S_SUBCMD_COUNT = 0x7, +enum ec_codec_cap { + EC_CODEC_CAP_WOV_AUDIO_SHM = 0, + EC_CODEC_CAP_WOV_LANG_SHM = 1, + EC_CODEC_CAP_LAST = 32, }; -enum ec_sample_depth_value { - EC_CODEC_SAMPLE_DEPTH_16 = 0, - EC_CODEC_SAMPLE_DEPTH_24 = 1, +enum ec_codec_shm_id { + EC_CODEC_SHM_ID_WOV_AUDIO = 0x0, + EC_CODEC_SHM_ID_WOV_LANG = 0x1, + EC_CODEC_SHM_ID_LAST, }; -enum ec_i2s_config { - EC_DAI_FMT_I2S = 0, - EC_DAI_FMT_RIGHT_J = 1, - EC_DAI_FMT_LEFT_J = 2, - EC_DAI_FMT_PCM_A = 3, - EC_DAI_FMT_PCM_B = 4, - EC_DAI_FMT_PCM_TDM = 5, +enum ec_codec_shm_type { + EC_CODEC_SHM_TYPE_EC_RAM = 0x0, + EC_CODEC_SHM_TYPE_SYSTEM_RAM = 0x1, }; -/* - * For subcommand EC_CODEC_GET_GAIN. - */ -struct __ec_align1 ec_codec_i2s_gain { +struct __ec_align1 ec_param_ec_codec_get_shm_addr { + uint8_t shm_id; + uint8_t reserved[3]; +}; + +struct __ec_align4 ec_param_ec_codec_set_shm_addr { + uint64_t phys_addr; + uint32_t len; + uint8_t shm_id; + uint8_t reserved[3]; +}; + +struct __ec_align4 ec_param_ec_codec { + uint8_t cmd; /* enum ec_codec_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_get_shm_addr + get_shm_addr_param; + struct ec_param_ec_codec_set_shm_addr + set_shm_addr_param; + }; +}; + +struct __ec_align4 ec_response_ec_codec_get_capabilities { + uint32_t capabilities; +}; + +struct __ec_align4 ec_response_ec_codec_get_shm_addr { + uint64_t phys_addr; + uint32_t len; + uint8_t type; + uint8_t reserved[3]; +}; + +/*****************************************************************************/ + +/* Commands for DMIC on audio codec. */ +#define EC_CMD_EC_CODEC_DMIC 0x00BD + +enum ec_codec_dmic_subcmd { + EC_CODEC_DMIC_GET_MAX_GAIN = 0x0, + EC_CODEC_DMIC_SET_GAIN_IDX = 0x1, + EC_CODEC_DMIC_GET_GAIN_IDX = 0x2, + EC_CODEC_DMIC_SUBCMD_COUNT, +}; + +enum ec_codec_dmic_channel { + EC_CODEC_DMIC_CHANNEL_0 = 0x0, + EC_CODEC_DMIC_CHANNEL_1 = 0x1, + EC_CODEC_DMIC_CHANNEL_2 = 0x2, + EC_CODEC_DMIC_CHANNEL_3 = 0x3, + EC_CODEC_DMIC_CHANNEL_4 = 0x4, + EC_CODEC_DMIC_CHANNEL_5 = 0x5, + EC_CODEC_DMIC_CHANNEL_6 = 0x6, + EC_CODEC_DMIC_CHANNEL_7 = 0x7, + EC_CODEC_DMIC_CHANNEL_COUNT, +}; + +struct __ec_align1 ec_param_ec_codec_dmic_set_gain_idx { + uint8_t channel; /* enum ec_codec_dmic_channel */ + uint8_t gain; + uint8_t reserved[2]; +}; + +struct __ec_align1 ec_param_ec_codec_dmic_get_gain_idx { + uint8_t channel; /* enum ec_codec_dmic_channel */ + uint8_t reserved[3]; +}; + +struct __ec_align4 ec_param_ec_codec_dmic { + uint8_t cmd; /* enum ec_codec_dmic_subcmd */ + uint8_t reserved[3]; + + union { + struct ec_param_ec_codec_dmic_set_gain_idx + set_gain_idx_param; + struct ec_param_ec_codec_dmic_get_gain_idx + get_gain_idx_param; + }; +}; + +struct __ec_align1 ec_response_ec_codec_dmic_get_max_gain { + uint8_t max_gain; +}; + +struct __ec_align1 ec_response_ec_codec_dmic_get_gain_idx { + uint8_t gain; +}; + +/*****************************************************************************/ + +/* Commands for I2S RX on audio codec. */ + +#define EC_CMD_EC_CODEC_I2S_RX 0x00BE + +enum ec_codec_i2s_rx_subcmd { + EC_CODEC_I2S_RX_ENABLE = 0x0, + EC_CODEC_I2S_RX_DISABLE = 0x1, + EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2, + EC_CODEC_I2S_RX_SET_DAIFMT = 0x3, + EC_CODEC_I2S_RX_SET_BCLK = 0x4, + EC_CODEC_I2S_RX_SUBCMD_COUNT, +}; + +enum ec_codec_i2s_rx_sample_depth { + EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1, + EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT, +}; + +enum ec_codec_i2s_rx_daifmt { + EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0, + EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1, + EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2, + EC_CODEC_I2S_RX_DAIFMT_COUNT, +}; + +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth { + uint8_t depth; + uint8_t reserved[3]; +}; + +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain { uint8_t left; uint8_t right; + uint8_t reserved[2]; }; -struct __ec_todo_unpacked ec_param_codec_i2s_tdm { - int16_t ch0_delay; /* 0 to 496 */ - int16_t ch1_delay; /* -1 to 496 */ - uint8_t adjacent_to_ch0; - uint8_t adjacent_to_ch1; +struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt { + uint8_t daifmt; + uint8_t reserved[3]; }; -struct __ec_todo_packed ec_param_codec_i2s { - /* enum ec_codec_i2s_subcmd */ - uint8_t cmd; +struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk { + uint32_t bclk; +}; + +struct __ec_align4 ec_param_ec_codec_i2s_rx { + uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */ + uint8_t reserved[3]; + union { - /* - * EC_CODEC_SET_SAMPLE_DEPTH - * Value should be one of ec_sample_depth_value. - */ - uint8_t depth; + struct ec_param_ec_codec_i2s_rx_set_sample_depth + set_sample_depth_param; + struct ec_param_ec_codec_i2s_rx_set_daifmt + set_daifmt_param; + struct ec_param_ec_codec_i2s_rx_set_bclk + set_bclk_param; + }; +}; - /* - * EC_CODEC_SET_GAIN - * Value should be 0~43 for both channels. - */ - struct ec_codec_i2s_gain gain; +/*****************************************************************************/ +/* Commands for WoV on audio codec. */ + +#define EC_CMD_EC_CODEC_WOV 0x00BF + +enum ec_codec_wov_subcmd { + EC_CODEC_WOV_SET_LANG = 0x0, + EC_CODEC_WOV_SET_LANG_SHM = 0x1, + EC_CODEC_WOV_GET_LANG = 0x2, + EC_CODEC_WOV_ENABLE = 0x3, + EC_CODEC_WOV_DISABLE = 0x4, + EC_CODEC_WOV_READ_AUDIO = 0x5, + EC_CODEC_WOV_READ_AUDIO_SHM = 0x6, + EC_CODEC_WOV_SUBCMD_COUNT, +}; - /* - * EC_CODEC_I2S_ENABLE - * 1 to enable, 0 to disable. - */ - uint8_t i2s_enable; +/* + * @hash is SHA256 of the whole language model. + * @total_len indicates the length of whole language model. + * @offset is the cursor from the beginning of the model. + * @buf is the packet buffer. + * @len denotes how many bytes in the buf. + */ +struct __ec_align4 ec_param_ec_codec_wov_set_lang { + uint8_t hash[32]; + uint32_t total_len; + uint32_t offset; + uint8_t buf[128]; + uint32_t len; +}; - /* - * EC_CODEC_I2S_SET_CONFIG - * Value should be one of ec_i2s_config. - */ - uint8_t i2s_config; +struct __ec_align4 ec_param_ec_codec_wov_set_lang_shm { + uint8_t hash[32]; + uint32_t total_len; +}; - /* - * EC_CODEC_I2S_SET_TDM_CONFIG - * Value should be one of ec_i2s_config. - */ - struct ec_param_codec_i2s_tdm tdm_param; +struct __ec_align4 ec_param_ec_codec_wov { + uint8_t cmd; /* enum ec_codec_wov_subcmd */ + uint8_t reserved[3]; - /* - * EC_CODEC_I2S_SET_BCLK - */ - uint32_t bclk; + union { + struct ec_param_ec_codec_wov_set_lang + set_lang_param; + struct ec_param_ec_codec_wov_set_lang_shm + set_lang_shm_param; }; }; +struct __ec_align4 ec_response_ec_codec_wov_get_lang { + uint8_t hash[32]; +}; + +struct __ec_align4 ec_response_ec_codec_wov_read_audio { + uint8_t buf[128]; + uint32_t len; +}; + +struct __ec_align4 ec_response_ec_codec_wov_read_audio_shm { + uint32_t offset; + uint32_t len; +}; /*****************************************************************************/ /* System commands */ diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index c679f6116580..b65220685920 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -83,6 +83,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data( const struct snd_dmaengine_dai_dma_data *dma_data, struct dma_slave_config *config); +int snd_dmaengine_pcm_refine_runtime_hwparams( + struct snd_pcm_substream *substream, + struct snd_dmaengine_dai_dma_data *dma_data, + struct snd_pcm_hardware *hw, + struct dma_chan *chan); /* * Try to request the DMA channel using compat_request_channel or diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 9a0393cf024c..ac18f428eda6 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -254,6 +254,7 @@ struct hda_codec { unsigned int force_pin_prefix:1; /* Add location prefix */ unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ + unsigned int mst_no_extra_pcms:1; /* no backup PCMs for DP-MST */ #ifdef CONFIG_PM unsigned long power_on_acct; diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 6758fc12fa84..0feaf16e6ac0 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -10,6 +10,7 @@ struct snd_pcm_substream; struct snd_pcm_hw_params; struct snd_soc_pcm_runtime; struct snd_pcm; +struct snd_soc_component; extern int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); @@ -23,8 +24,29 @@ extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); extern int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream); extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm); -extern int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd); -extern const struct snd_pcm_ops pxa2xx_pcm_ops; +extern void pxa2xx_soc_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm); +extern int pxa2xx_soc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); +extern int pxa2xx_soc_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +extern int pxa2xx_soc_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +extern int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +extern int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +extern int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +extern int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd); +extern snd_pcm_uframes_t +pxa2xx_soc_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +extern int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma); /* AC97 */ diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h index bf2ee75aabb1..bc2c31734df1 100644 --- a/include/sound/rt5682.h +++ b/include/sound/rt5682.h @@ -31,6 +31,7 @@ struct rt5682_platform_data { enum rt5682_dmic1_data_pin dmic1_data_pin; enum rt5682_dmic1_clk_pin dmic1_clk_pin; enum rt5682_jd_src jd_src; + unsigned int btndet_delay; }; #endif diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 31f76b6abf71..bbdd1542d6f1 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -8,6 +8,7 @@ #ifndef __SIMPLE_CARD_UTILS_H #define __SIMPLE_CARD_UTILS_H +#include <linux/clk.h> #include <sound/soc.h> #define asoc_simple_init_hp(card, sjack, prefix) \ diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index 6c9929abd90b..20c0bee3b959 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -24,9 +24,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[]; /* * generic table used for HDA codec-based platforms, possibly with diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 35b38e41e5b2..c4c997bd0379 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -60,12 +60,14 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @acpi_ipc_irq_index: used for BYT-CR detection * @platform: string used for HDaudio codec support * @codec_mask: used for HDAudio support + * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver */ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; const char *platform; u32 codec_mask; u32 dmic_num; + bool common_hdmi_codec_drv; }; /** @@ -75,6 +77,7 @@ struct snd_soc_acpi_mach_params { * all firmware/topology related fields. * * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @link_mask: describes required board layout, e.g. for SoundWire. * @drv_name: machine driver name * @fw_filename: firmware file name. Used when SOF is not enabled. * @board: board name @@ -90,6 +93,7 @@ struct snd_soc_acpi_mach_params { /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { const u8 id[ACPI_ID_LEN]; + const u32 link_mask; const char *drv_name; const char *fw_filename; const char *board; diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 5d80b2eef525..f8fadf0e696a 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -3,10 +3,6 @@ * soc-component.h * * Copyright (c) 2019 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __SOC_COMPONENT_H #define __SOC_COMPONENT_H @@ -51,8 +47,10 @@ struct snd_soc_component_driver { unsigned int reg, unsigned int val); /* pcm creation and destruction */ - int (*pcm_new)(struct snd_soc_pcm_runtime *rtd); - void (*pcm_free)(struct snd_pcm *pcm); + int (*pcm_construct)(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); + void (*pcm_destruct)(struct snd_soc_component *component, + struct snd_pcm *pcm); /* component wide operations */ int (*set_sysclk)(struct snd_soc_component *component, @@ -74,7 +72,40 @@ struct snd_soc_component_driver { int (*set_bias_level)(struct snd_soc_component *component, enum snd_soc_bias_level level); - const struct snd_pcm_ops *ops; + int (*open)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*close)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*ioctl)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned int cmd, void *arg); + int (*hw_params)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + int (*hw_free)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*prepare)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*trigger)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd); + snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*get_time_info)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct timespec *system_ts, + struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report); + int (*copy_user)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes); + struct page *(*page)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset); + int (*mmap)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma); + const struct snd_compr_ops *compr_ops; /* probe ordering - for components with runtime dependencies */ @@ -381,7 +412,7 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream, unsigned long offset); int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -int snd_soc_pcm_component_new(struct snd_pcm *pcm); -void snd_soc_pcm_component_free(struct snd_pcm *pcm); +int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd); +void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd); #endif /* __SOC_COMPONENT_H */ diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index e55aeb00ce2d..b654ebfc8766 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -103,15 +103,15 @@ struct snd_soc_dpcm_runtime { int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ }; -#define for_each_dpcm_fe(be, stream, dpcm) \ - list_for_each_entry(dpcm, &(be)->dpcm[stream].fe_clients, list_fe) - -#define for_each_dpcm_be(fe, stream, dpcm) \ - list_for_each_entry(dpcm, &(fe)->dpcm[stream].be_clients, list_be) -#define for_each_dpcm_be_safe(fe, stream, dpcm, _dpcm) \ - list_for_each_entry_safe(dpcm, _dpcm, &(fe)->dpcm[stream].be_clients, list_be) -#define for_each_dpcm_be_rollback(fe, stream, dpcm) \ - list_for_each_entry_continue_reverse(dpcm, &(fe)->dpcm[stream].be_clients, list_be) +#define for_each_dpcm_fe(be, stream, _dpcm) \ + list_for_each_entry(_dpcm, &(be)->dpcm[stream].fe_clients, list_fe) + +#define for_each_dpcm_be(fe, stream, _dpcm) \ + list_for_each_entry(_dpcm, &(fe)->dpcm[stream].be_clients, list_be) +#define for_each_dpcm_be_safe(fe, stream, _dpcm, __dpcm) \ + list_for_each_entry_safe(_dpcm, __dpcm, &(fe)->dpcm[stream].be_clients, list_be) +#define for_each_dpcm_be_rollback(fe, stream, _dpcm) \ + list_for_each_entry_continue_reverse(_dpcm, &(fe)->dpcm[stream].be_clients, list_be) /* can this BE stop and free */ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, diff --git a/include/sound/soc.h b/include/sound/soc.h index f264c6509f00..c28a1ed5e8df 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -299,6 +299,12 @@ .put = snd_soc_bytes_put, .private_value = \ ((unsigned long)&(struct soc_bytes) \ {.base = xbase, .num_regs = xregs }) } +#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = xhandler_get, \ + .put = xhandler_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) \ + {.base = xbase, .num_regs = xregs }) } #define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -739,10 +745,12 @@ struct snd_soc_rtdcom_list { struct snd_soc_component* snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, const char *driver_name); -#define for_each_rtdcom(rtd, rtdcom) \ - list_for_each_entry(rtdcom, &(rtd)->component_list, list) -#define for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) \ - list_for_each_entry_safe(rtdcom1, rtdcom2, &(rtd)->component_list, list) +#define for_each_rtd_components(rtd, rtdcom, _component) \ + for (rtdcom = list_first_entry(&(rtd)->component_list, \ + typeof(*rtdcom), list); \ + (&rtdcom->list != &(rtd)->component_list) && \ + (_component = rtdcom->component); \ + rtdcom = list_next_entry(rtdcom, list)) struct snd_soc_dai_link_component { const char *name; @@ -845,7 +853,9 @@ struct snd_soc_dai_link { unsigned int ignore:1; struct list_head list; /* DAI link list of the soc card */ +#ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; /* For topology */ +#endif }; #define for_each_link_codecs(link, i, codec) \ for ((i) = 0; \ @@ -978,6 +988,7 @@ struct snd_soc_card { const char *name; const char *long_name; const char *driver_name; + const char *components; char dmi_longname[80]; char topology_shortname[32]; @@ -1148,7 +1159,6 @@ struct snd_soc_pcm_runtime { struct list_head component_list; /* list of connected components */ /* bit field */ - unsigned int dev_registered:1; unsigned int pop_wait:1; unsigned int fe_compr:1; /* for Dynamic PCM */ }; @@ -1168,7 +1178,9 @@ struct soc_mixer_control { unsigned int sign_bit; unsigned int invert:1; unsigned int autodisable:1; +#ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; +#endif }; struct soc_bytes { @@ -1179,8 +1191,9 @@ struct soc_bytes { struct soc_bytes_ext { int max; +#ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; - +#endif /* used for TLV byte control */ int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes, unsigned int size); @@ -1204,7 +1217,9 @@ struct soc_enum { const char * const *texts; const unsigned int *values; unsigned int autodisable:1; +#ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; +#endif }; /* device driver data */ @@ -1325,8 +1340,10 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, int id, const char *name, const char *stream_name); -int snd_soc_register_dai(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv); +struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, + struct snd_soc_dai_driver *dai_drv, + bool legacy_dai_naming); +void snd_soc_unregister_dai(struct snd_soc_dai *dai); struct snd_soc_dai *snd_soc_find_dai( const struct snd_soc_dai_link_component *dlc); @@ -1391,6 +1408,11 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm) mutex_unlock(&dapm->card->dapm_mutex); } +/* bypass */ +int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned int cmd, void *arg); + #include <sound/soc-component.h> #endif diff --git a/include/sound/sof.h b/include/sound/sof.h index 4640566b54fe..479101736ee0 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -61,6 +61,9 @@ struct sof_dev_desc { /* list of machines using this configuration */ struct snd_soc_acpi_mach *machines; + /* alternate list of machines using this configuration */ + struct snd_soc_acpi_mach *alt_machines; + /* Platform resource indexes in BAR / ACPI resources. */ /* Must set to -1 if not used - add new items to end */ int resindex_lpe_base; diff --git a/include/sound/sof/dai-imx.h b/include/sound/sof/dai-imx.h new file mode 100644 index 000000000000..e02fb0b0fae1 --- /dev/null +++ b/include/sound/sof/dai-imx.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright 2019 NXP + * + * Author: Daniel Baluta <daniel.baluta@nxp.com> + */ + +#ifndef __INCLUDE_SOUND_SOF_DAI_IMX_H__ +#define __INCLUDE_SOUND_SOF_DAI_IMX_H__ + +#include <sound/sof/header.h> + +/* ESAI Configuration Request - SOF_IPC_DAI_ESAI_CONFIG */ +struct sof_ipc_dai_esai_params { + struct sof_ipc_hdr hdr; + + /* MCLK */ + uint16_t reserved1; + uint16_t mclk_id; + uint32_t mclk_direction; + + uint32_t mclk_rate; /* MCLK frequency in Hz */ + uint32_t fsync_rate; /* FSYNC frequency in Hz */ + uint32_t bclk_rate; /* BCLK frequency in Hz */ + + /* TDM */ + uint32_t tdm_slots; + uint32_t rx_slots; + uint32_t tx_slots; + uint16_t tdm_slot_width; + uint16_t reserved2; /* alignment */ +} __packed; + +#endif diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 0f1235022146..c229565767e5 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -11,6 +11,7 @@ #include <sound/sof/header.h> #include <sound/sof/dai-intel.h> +#include <sound/sof/dai-imx.h> /* * DAI Configuration. @@ -73,6 +74,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_dmic_params dmic; struct sof_ipc_dai_hda_params hda; struct sof_ipc_dai_alh_params alh; + struct sof_ipc_dai_esai_params esai; }; } __packed; diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 10f00c08dbb7..bf3edd9c08b4 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -9,6 +9,7 @@ #ifndef __INCLUDE_SOUND_SOF_HEADER_H__ #define __INCLUDE_SOUND_SOF_HEADER_H__ +#include <linux/types.h> #include <uapi/sound/sof/abi.h> /** \addtogroup sof_uapi uAPI @@ -74,6 +75,7 @@ #define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005) #define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006) #define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007) +#define SOF_IPC_PM_GATE SOF_CMD_TYPE(0x008) /* component runtime config - multiple different types */ #define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001) diff --git a/include/sound/sof/pm.h b/include/sound/sof/pm.h index 003879401d63..3cf2e0f39d94 100644 --- a/include/sound/sof/pm.h +++ b/include/sound/sof/pm.h @@ -45,4 +45,12 @@ struct sof_ipc_pm_core_config { uint32_t enable_mask; } __packed; +struct sof_ipc_pm_gate { + struct sof_ipc_cmd_hdr hdr; + uint32_t flags; /* platform specific */ + + /* reserved for future use */ + uint32_t reserved[5]; +} __packed; + #endif diff --git a/include/sound/sof/stream.h b/include/sound/sof/stream.h index 0b71b381b952..7facefb541b3 100644 --- a/include/sound/sof/stream.h +++ b/include/sound/sof/stream.h @@ -83,10 +83,10 @@ struct sof_ipc_stream_params { uint16_t sample_valid_bytes; uint16_t sample_container_bytes; - /* for notifying host period has completed - 0 means no period IRQ */ uint32_t host_period_bytes; + uint16_t no_stream_position; /**< 1 means don't send stream position */ - uint32_t reserved[2]; + uint16_t reserved[3]; uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */ } __packed; diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h index 14074405f501..88ac1870510e 100644 --- a/include/sound/wm8904.h +++ b/include/sound/wm8904.h @@ -120,7 +120,7 @@ * DRC configurations are specified with a label and a set of register * values to write (the enable bits will be ignored). At runtime an * enumerated control will be presented for each DRC block allowing - * the user to choose the configration to use. + * the user to choose the configuration to use. * * Configurations may be generated by hand or by using the DRC control * panel provided by the WISCE - see http://www.wolfsonmicro.com/wisce/ diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index 3d4d6de66a17..9c96fb0e4d90 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -317,12 +317,22 @@ struct snd_enc_generic { __s32 reserved[15]; /* Can be used for SND_AUDIOCODEC_BESPOKE */ } __attribute__((packed, aligned(4))); +struct snd_dec_flac { + __u16 sample_size; + __u16 min_blk_size; + __u16 max_blk_size; + __u16 min_frame_size; + __u16 max_frame_size; + __u16 reserved; +} __attribute__((packed, aligned(4))); + union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; struct snd_enc_real real; struct snd_enc_flac flac; struct snd_enc_generic generic; + struct snd_dec_flac flac_d; } __attribute__((packed, aligned(4))); /** struct snd_codec_desc - description of codec capabilities diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index a0fe0d4c4b66..ebfdc20ca081 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,7 +26,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 10 +#define SOF_ABI_MINOR 11 #define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 8f996857fb24..76883e6fb750 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -111,7 +111,14 @@ /* TODO: Add SAI tokens */ /* ESAI */ -#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100 -/* TODO: Add ESAI tokens */ +#define SOF_TKN_IMX_ESAI_MCLK_ID 1100 + +/* Stream */ +#define SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3 1200 +#define SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3 1201 + +/* Led control for mute switches */ +#define SOF_TKN_MUTE_LED_USE 1300 +#define SOF_TKN_MUTE_LED_DIRECTION 1301 #endif diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 54500bd098f9..a86c95d89824 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -175,7 +175,15 @@ void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) } EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); -int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) +void pxa2xx_soc_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + pxa2xx_pcm_free_dma_buffers(pcm); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_free); + +int pxa2xx_soc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -203,18 +211,64 @@ int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) } EXPORT_SYMBOL(pxa2xx_soc_pcm_new); -const struct snd_pcm_ops pxa2xx_pcm_ops = { - .open = pxa2xx_pcm_open, - .close = pxa2xx_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pxa2xx_pcm_hw_params, - .hw_free = pxa2xx_pcm_hw_free, - .prepare = pxa2xx_pcm_prepare, - .trigger = pxa2xx_pcm_trigger, - .pointer = pxa2xx_pcm_pointer, - .mmap = pxa2xx_pcm_mmap, -}; -EXPORT_SYMBOL(pxa2xx_pcm_ops); +int pxa2xx_soc_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return pxa2xx_pcm_open(substream); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_open); + +int pxa2xx_soc_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return pxa2xx_pcm_close(substream); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_close); + +int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return pxa2xx_pcm_hw_params(substream, params); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params); + +int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return pxa2xx_pcm_hw_free(substream); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_free); + +int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return pxa2xx_pcm_prepare(substream); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare); + +int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + return pxa2xx_pcm_trigger(substream, cmd); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger); + +snd_pcm_uframes_t +pxa2xx_soc_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return pxa2xx_pcm_pointer(substream); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer); + +int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return pxa2xx_pcm_mmap(substream, vma); +} +EXPORT_SYMBOL(pxa2xx_soc_pcm_mmap); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("Intel PXA2xx sound library"); diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 89a05926ac73..5749a8a49784 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -369,4 +369,87 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); +/** + * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params + * @substream: PCM substream + * @dma_data: DAI DMA data + * @hw: PCM hw params + * @chan: DMA channel to use for data transfers + * + * Returns 0 on success, a negative error code otherwise. + * + * This function will query DMA capability, then refine the pcm hardware + * parameters. + */ +int snd_dmaengine_pcm_refine_runtime_hwparams( + struct snd_pcm_substream *substream, + struct snd_dmaengine_dai_dma_data *dma_data, + struct snd_pcm_hardware *hw, + struct dma_chan *chan) +{ + struct dma_slave_caps dma_caps; + u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + snd_pcm_format_t i; + int ret = 0; + + if (!hw || !chan || !dma_data) + return -EINVAL; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret == 0) { + if (dma_caps.cmd_pause && dma_caps.cmd_resume) + hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) + hw->info |= SNDRV_PCM_INFO_BATCH; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + addr_widths = dma_caps.dst_addr_widths; + else + addr_widths = dma_caps.src_addr_widths; + } + + /* + * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep + * hw.formats set to 0, meaning no restrictions are in place. + * In this case it's the responsibility of the DAI driver to + * provide the supported format information. + */ + if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) + /* + * Prepare formats mask for valid/allowed sample types. If the + * dma does not have support for the given physical word size, + * it needs to be masked out so user space can not use the + * format which produces corrupted audio. + * In case the dma driver does not implement the slave_caps the + * default assumption is that it supports 1, 2 and 4 bytes + * widths. + */ + for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) { + int bits = snd_pcm_format_physical_width(i); + + /* + * Enable only samples with DMA supported physical + * widths + */ + switch (bits) { + case 8: + case 16: + case 24: + case 32: + case 64: + if (addr_widths & (1 << (bits / 8))) + hw->formats |= pcm_format_to_bits(i); + break; + default: + /* Unsupported types */ + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams); + MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 78bd2e3722c7..d24bd95c6e95 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2075,15 +2075,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int idx; + int idx, pcm_num; /* * for non-mst mode, pcm number is the same as before - * for DP MST mode, pcm number is (nid number + dev_num - 1) - * dev_num is the device entry number in a pin - * + * for DP MST mode without extra PCM, pcm number is same + * for DP MST mode with extra PCMs, pcm number is + * (nid number + dev_num - 1) + * dev_num is the device entry number in a pin */ - for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) { + + if (codec->mst_no_extra_pcms) + pcm_num = spec->num_nids; + else + pcm_num = spec->num_nids + spec->dev_num - 1; + + codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num); + + for (idx = 0; idx < pcm_num; idx++) { struct hda_pcm *info; struct hda_pcm_stream *pstr; diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 52225b4b6382..4b9a27e25206 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -759,14 +759,12 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) return IRQ_NONE; } -static int acp_dma_open(struct snd_pcm_substream *substream) +static int acp_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { u16 bank; int ret = 0; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct audio_drv_data *intr_data = dev_get_drvdata(component->dev); struct audio_substream_data *adata = kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL); @@ -834,7 +832,8 @@ static int acp_dma_open(struct snd_pcm_substream *substream) return 0; } -static int acp_dma_hw_params(struct snd_pcm_substream *substream, +static int acp_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { int status; @@ -843,8 +842,6 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); struct snd_soc_card *card = prtd->card; struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card); @@ -995,7 +992,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, return status; } -static int acp_dma_hw_free(struct snd_pcm_substream *substream) +static int acp_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } @@ -1011,7 +1009,8 @@ static u64 acp_get_byte_count(struct audio_substream_data *rtd) return byte_count.bytescount; } -static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { u32 buffersize; u32 pos = 0; @@ -1053,13 +1052,15 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, pos); } -static int acp_dma_mmap(struct snd_pcm_substream *substream, +static int acp_dma_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { return snd_pcm_lib_default_mmap(substream, vma); } -static int acp_dma_prepare(struct snd_pcm_substream *substream) +static int acp_dma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; @@ -1086,7 +1087,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) return 0; } -static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) +static int acp_dma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { int ret; @@ -1132,10 +1134,9 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) +static int acp_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, - DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); struct device *parent = component->dev->parent; @@ -1158,14 +1159,12 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static int acp_dma_close(struct snd_pcm_substream *substream) +static int acp_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { u16 bank; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -1216,22 +1215,18 @@ static int acp_dma_close(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops acp_dma_ops = { - .open = acp_dma_open, - .close = acp_dma_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = acp_dma_hw_params, - .hw_free = acp_dma_hw_free, - .trigger = acp_dma_trigger, - .pointer = acp_dma_pointer, - .mmap = acp_dma_mmap, - .prepare = acp_dma_prepare, -}; - static const struct snd_soc_component_driver acp_asoc_platform = { - .name = DRV_NAME, - .ops = &acp_dma_ops, - .pcm_new = acp_dma_new, + .name = DRV_NAME, + .open = acp_dma_open, + .close = acp_dma_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = acp_dma_hw_params, + .hw_free = acp_dma_hw_free, + .trigger = acp_dma_trigger, + .pointer = acp_dma_pointer, + .mmap = acp_dma_mmap, + .prepare = acp_dma_prepare, + .pcm_construct = acp_dma_new, }; static int acp_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index bc4dfafdfcd1..60709e3ba99d 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -275,16 +275,12 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); } -static int acp3x_dma_open(struct snd_pcm_substream *substream) +static int acp3x_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { int ret = 0; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct i2s_dev_data *adata = dev_get_drvdata(component->dev); - struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance), GFP_KERNEL); if (!i2s_data) @@ -334,7 +330,8 @@ static u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction) return byte_count; } -static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, +static int acp3x_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { int status; @@ -362,7 +359,8 @@ static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, return status; } -static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { u32 pos = 0; u32 buffersize = 0; @@ -379,33 +377,32 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, pos); } -static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd) +static int acp3x_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, - DRV_NAME); struct device *parent = component->dev->parent; snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, parent, MIN_BUFFER, MAX_BUFFER); return 0; } -static int acp3x_dma_hw_free(struct snd_pcm_substream *substream) +static int acp3x_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static int acp3x_dma_mmap(struct snd_pcm_substream *substream, +static int acp3x_dma_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { return snd_pcm_lib_default_mmap(substream, vma); } -static int acp3x_dma_close(struct snd_pcm_substream *substream) +static int acp3x_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *prtd = substream->private_data; struct i2s_stream_instance *rtd = substream->runtime->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct i2s_dev_data *adata = dev_get_drvdata(component->dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -422,17 +419,6 @@ static int acp3x_dma_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops acp3x_dma_ops = { - .open = acp3x_dma_open, - .close = acp3x_dma_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = acp3x_dma_hw_params, - .hw_free = acp3x_dma_hw_free, - .pointer = acp3x_dma_pointer, - .mmap = acp3x_dma_mmap, -}; - - static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -610,9 +596,15 @@ static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { }; static const struct snd_soc_component_driver acp3x_i2s_component = { - .name = DRV_NAME, - .ops = &acp3x_dma_ops, - .pcm_new = acp3x_dma_new, + .name = DRV_NAME, + .open = acp3x_dma_open, + .close = acp3x_dma_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = acp3x_dma_hw_params, + .hw_free = acp3x_dma_hw_free, + .pointer = acp3x_dma_pointer, + .mmap = acp3x_dma_mmap, + .pcm_construct = acp3x_dma_new, }; static int acp3x_audio_probe(struct platform_device *pdev) @@ -631,7 +623,7 @@ static int acp3x_audio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); - return -ENODEV; + return -ENODEV; } adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL); diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index ed095af866db..18a2fd02fffe 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -56,15 +56,17 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, return 0; } -static int atmel_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int atmel_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { return remap_pfn_range(vma, vma->vm_start, substream->dma_buffer.addr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); } -static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int atmel_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -93,7 +95,8 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) return ret; } -static void atmel_pcm_free(struct snd_pcm *pcm) +static void atmel_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -196,8 +199,9 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, /*--------------------------------------------------------------------------*\ * PCM operations \*--------------------------------------------------------------------------*/ -static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int atmel_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct atmel_runtime_data *prtd = runtime->private_data; @@ -225,7 +229,8 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) +static int atmel_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; @@ -239,7 +244,8 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int atmel_pcm_prepare(struct snd_pcm_substream *substream) +static int atmel_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; @@ -251,8 +257,8 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream) return 0; } -static int atmel_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) +static int atmel_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *rtd = substream->runtime; struct atmel_runtime_data *prtd = rtd->private_data; @@ -317,8 +323,8 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, return ret; } -static snd_pcm_uframes_t atmel_pcm_pointer( - struct snd_pcm_substream *substream) +static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct atmel_runtime_data *prtd = runtime->private_data; @@ -335,7 +341,8 @@ static snd_pcm_uframes_t atmel_pcm_pointer( return x; } -static int atmel_pcm_open(struct snd_pcm_substream *substream) +static int atmel_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct atmel_runtime_data *prtd; @@ -360,7 +367,8 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream) return ret; } -static int atmel_pcm_close(struct snd_pcm_substream *substream) +static int atmel_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; @@ -368,22 +376,18 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops atmel_pcm_ops = { +static const struct snd_soc_component_driver atmel_soc_platform = { .open = atmel_pcm_open, .close = atmel_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = atmel_pcm_hw_params, .hw_free = atmel_pcm_hw_free, .prepare = atmel_pcm_prepare, .trigger = atmel_pcm_trigger, .pointer = atmel_pcm_pointer, .mmap = atmel_pcm_mmap, -}; - -static struct snd_soc_component_driver atmel_soc_platform = { - .ops = &atmel_pcm_ops, - .pcm_new = atmel_pcm_new, - .pcm_free = atmel_pcm_free, + .pcm_construct = atmel_pcm_new, + .pcm_destruct = atmel_pcm_free, }; int atmel_pcm_pdc_platform_register(struct device *dev) diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index d56092a5ee11..4553108ec92a 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -182,15 +182,15 @@ out: return 0; } -static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss) +static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss, + struct snd_soc_component *component) { - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component); return &pcd[ss->stream]; } -static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, +static int au1xpsc_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -202,7 +202,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, goto out; stype = substream->stream; - pcd = to_dmadata(substream); + pcd = to_dmadata(substream, component); DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu " "runtime->min_align %lu\n", @@ -232,15 +232,17 @@ out: return ret; } -static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream) +static int au1xpsc_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_lib_free_pages(substream); return 0; } -static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream) +static int au1xpsc_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream); + struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component); au1xxx_dbdma_reset(pcd->ddma_chan); @@ -255,9 +257,10 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream) return 0; } -static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int au1xpsc_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { - u32 c = to_dmadata(substream)->ddma_chan; + u32 c = to_dmadata(substream, component)->ddma_chan; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -275,14 +278,17 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t -au1xpsc_pcm_pointer(struct snd_pcm_substream *substream) +au1xpsc_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos); + return bytes_to_frames(substream->runtime, + to_dmadata(substream, component)->pos); } -static int au1xpsc_pcm_open(struct snd_pcm_substream *substream) +static int au1xpsc_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream); + struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component); struct snd_soc_pcm_runtime *rtd = substream->private_data; int stype = substream->stream, *dmaids; @@ -296,24 +302,15 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int au1xpsc_pcm_close(struct snd_pcm_substream *substream) +static int au1xpsc_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - au1x_pcm_dbdma_free(to_dmadata(substream)); + au1x_pcm_dbdma_free(to_dmadata(substream, component)); return 0; } -static const struct snd_pcm_ops au1xpsc_pcm_ops = { - .open = au1xpsc_pcm_open, - .close = au1xpsc_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = au1xpsc_pcm_hw_params, - .hw_free = au1xpsc_pcm_hw_free, - .prepare = au1xpsc_pcm_prepare, - .trigger = au1xpsc_pcm_trigger, - .pointer = au1xpsc_pcm_pointer, -}; - -static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int au1xpsc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -327,8 +324,15 @@ static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd) /* au1xpsc audio platform */ static struct snd_soc_component_driver au1xpsc_soc_component = { .name = DRV_NAME, - .ops = &au1xpsc_pcm_ops, - .pcm_new = au1xpsc_pcm_new, + .open = au1xpsc_pcm_open, + .close = au1xpsc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = au1xpsc_pcm_hw_params, + .hw_free = au1xpsc_pcm_hw_free, + .prepare = au1xpsc_pcm_prepare, + .trigger = au1xpsc_pcm_trigger, + .pointer = au1xpsc_pcm_pointer, + .pcm_construct = au1xpsc_pcm_new, }; static int au1xpsc_pcm_drvprobe(struct platform_device *pdev) diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index 1e98cc4f9e27..49556eb409cd 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -174,22 +174,23 @@ static const struct snd_pcm_hardware alchemy_pcm_hardware = { .fifo_size = 16, }; -static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss) +static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss, + struct snd_soc_component *component) { - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); return snd_soc_component_get_drvdata(component); } -static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss) +static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss, + struct snd_soc_component *component) { - struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss); + struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component); return &(ctx->stream[ss->stream]); } -static int alchemy_pcm_open(struct snd_pcm_substream *substream) +static int alchemy_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream); + struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component); struct snd_soc_pcm_runtime *rtd = substream->private_data; int *dmaids, s = substream->stream; char *name; @@ -213,9 +214,10 @@ static int alchemy_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int alchemy_pcm_close(struct snd_pcm_substream *substream) +static int alchemy_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream); + struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component); int stype = substream->stream; ctx->stream[stype].substream = NULL; @@ -224,10 +226,11 @@ static int alchemy_pcm_close(struct snd_pcm_substream *substream) return 0; } -static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream, +static int alchemy_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct audio_stream *stream = ss_to_as(substream); + struct audio_stream *stream = ss_to_as(substream, component); int err; err = snd_pcm_lib_malloc_pages(substream, @@ -243,16 +246,18 @@ static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream, return err; } -static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream) +static int alchemy_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct audio_stream *stream = ss_to_as(substream); + struct audio_stream *stream = ss_to_as(substream, component); au1000_release_dma_link(stream); return snd_pcm_lib_free_pages(substream); } -static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int alchemy_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { - struct audio_stream *stream = ss_to_as(substream); + struct audio_stream *stream = ss_to_as(substream, component); int err = 0; switch (cmd) { @@ -269,9 +274,10 @@ static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return err; } -static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss) +static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { - struct audio_stream *stream = ss_to_as(ss); + struct audio_stream *stream = ss_to_as(ss, component); long location; location = get_dma_residue(stream->dma); @@ -281,17 +287,8 @@ static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss) return bytes_to_frames(ss->runtime, location); } -static const struct snd_pcm_ops alchemy_pcm_ops = { - .open = alchemy_pcm_open, - .close = alchemy_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = alchemy_pcm_hw_params, - .hw_free = alchemy_pcm_hw_free, - .trigger = alchemy_pcm_trigger, - .pointer = alchemy_pcm_pointer, -}; - -static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int alchemy_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -303,8 +300,14 @@ static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_component_driver alchemy_pcm_soc_component = { .name = DRV_NAME, - .ops = &alchemy_pcm_ops, - .pcm_new = alchemy_pcm_new, + .open = alchemy_pcm_open, + .close = alchemy_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = alchemy_pcm_hw_params, + .hw_free = alchemy_pcm_hw_free, + .trigger = alchemy_pcm_trigger, + .pointer = alchemy_pcm_pointer, + .pcm_construct = alchemy_pcm_new, }; static int alchemy_pcm_drvprobe(struct platform_device *pdev) diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c index 8966b02844dc..c65408085c1d 100644 --- a/sound/soc/bcm/cygnus-pcm.c +++ b/sound/soc/bcm/cygnus-pcm.c @@ -376,7 +376,8 @@ static void disable_intr(struct snd_pcm_substream *substream) } -static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int cygnus_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { int ret = 0; @@ -577,7 +578,8 @@ static irqreturn_t cygnus_dma_irq(int irq, void *data) return IRQ_HANDLED; } -static int cygnus_pcm_open(struct snd_pcm_substream *substream) +static int cygnus_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -613,7 +615,8 @@ static int cygnus_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int cygnus_pcm_close(struct snd_pcm_substream *substream) +static int cygnus_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct cygnus_aio_port *aio; @@ -633,8 +636,9 @@ static int cygnus_pcm_close(struct snd_pcm_substream *substream) return 0; } -static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int cygnus_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -649,7 +653,8 @@ static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream) +static int cygnus_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct cygnus_aio_port *aio; @@ -661,7 +666,8 @@ static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int cygnus_pcm_prepare(struct snd_pcm_substream *substream) +static int cygnus_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -694,7 +700,8 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream) return 0; } -static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct cygnus_aio_port *aio; unsigned int res = 0, cur = 0, base = 0; @@ -750,19 +757,8 @@ static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) return 0; } - -static const struct snd_pcm_ops cygnus_pcm_ops = { - .open = cygnus_pcm_open, - .close = cygnus_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = cygnus_pcm_hw_params, - .hw_free = cygnus_pcm_hw_free, - .prepare = cygnus_pcm_prepare, - .trigger = cygnus_pcm_trigger, - .pointer = cygnus_pcm_pointer, -}; - -static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm) +static void cygnus_dma_free_dma_buffers(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -788,7 +784,8 @@ static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm) } } -static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd) +static int cygnus_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -810,7 +807,7 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd) ret = cygnus_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) { - cygnus_dma_free_dma_buffers(pcm); + cygnus_dma_free_dma_buffers(component, pcm); return ret; } } @@ -819,9 +816,16 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd) } static struct snd_soc_component_driver cygnus_soc_platform = { - .ops = &cygnus_pcm_ops, - .pcm_new = cygnus_dma_new, - .pcm_free = cygnus_dma_free_dma_buffers, + .open = cygnus_pcm_open, + .close = cygnus_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = cygnus_pcm_hw_params, + .hw_free = cygnus_pcm_hw_free, + .prepare = cygnus_pcm_prepare, + .trigger = cygnus_pcm_trigger, + .pointer = cygnus_pcm_pointer, + .pcm_construct = cygnus_dma_new, + .pcm_destruct = cygnus_dma_free_dma_buffers, }; int cygnus_soc_platform_register(struct device *dev, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 229cc89f8c5a..ec01e4f12a78 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -34,6 +34,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_ADAU1977_I2C if I2C select SND_SOC_ADAU1701 if I2C select SND_SOC_ADAU7002 + select SND_SOC_ADAU7118_I2C if I2C + select SND_SOC_ADAU7118_HW select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4118 if I2C @@ -179,6 +181,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_STI_SAS select SND_SOC_TAS2552 if I2C + select SND_SOC_TAS2562 if I2C + select SND_SOC_TAS2770 if I2C select SND_SOC_TAS5086 if I2C select SND_SOC_TAS571X if I2C select SND_SOC_TAS5720 if I2C @@ -395,6 +399,33 @@ config SND_SOC_ADAU1977_I2C config SND_SOC_ADAU7002 tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter" +config SND_SOC_ADAU7118 + tristate + +config SND_SOC_ADAU7118_HW + tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode" + select SND_SOC_ADAU7118 + help + Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM + Converter. In this mode, the device works in standalone mode which + means that there is no bus to comunicate with it. Stereo mode is not + supported in this mode. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-adau7118-hw. + +config SND_SOC_ADAU7118_I2C + tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C" + depends on I2C + select SND_SOC_ADAU7118 + select REGMAP_I2C + help + Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM + Converter over I2C. This gives full support over the device. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-adau7118-i2c. + config SND_SOC_ADAV80X tristate @@ -478,6 +509,8 @@ config SND_SOC_CQ0093VC config SND_SOC_CROS_EC_CODEC tristate "codec driver for ChromeOS EC" depends on CROS_EC + select CRYPTO + select CRYPTO_SHA256 help If you say yes here you will get support for the ChromeOS Embedded Controller's Audio Codec. @@ -646,7 +679,8 @@ config SND_SOC_DA7210 tristate config SND_SOC_DA7213 - tristate + tristate "Dialog DA7213 CODEC" + depends on I2C config SND_SOC_DA7218 tristate @@ -1104,6 +1138,14 @@ config SND_SOC_TAS2552 tristate "Texas Instruments TAS2552 Mono Audio amplifier" depends on I2C +config SND_SOC_TAS2562 + tristate "Texas Instruments TAS2562 Mono Audio amplifier" + depends on I2C + +config SND_SOC_TAS2770 + tristate "Texas Instruments TAS2770 speaker amplifier" + depends on I2C + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c498373dcc5f..ddfd07071925 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -22,6 +22,9 @@ snd-soc-adau1977-objs := adau1977.o snd-soc-adau1977-spi-objs := adau1977-spi.o snd-soc-adau1977-i2c-objs := adau1977-i2c.o snd-soc-adau7002-objs := adau7002.o +snd-soc-adau7118-objs := adau7118.o +snd-soc-adau7118-i2c-objs := adau7118-i2c.o +snd-soc-adau7118-hw-objs := adau7118-hw.o snd-soc-adav80x-objs := adav80x.o snd-soc-adav801-objs := adav801.o snd-soc-adav803-objs := adav803.o @@ -196,6 +199,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tas2770-objs := tas2770.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -280,6 +284,7 @@ snd-soc-max98504-objs := max98504.o snd-soc-simple-amplifier-objs := simple-amplifier.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o +snd-soc-tas2562-objs := tas2562.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -304,6 +309,9 @@ obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o +obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o +obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o +obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o @@ -474,11 +482,13 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o +obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 977f5a63be3f..5ca9b744b7d8 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -28,6 +28,10 @@ #define ADAU1761_REC_MIXER_RIGHT1 0x400d #define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e #define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f +#define ADAU1761_ALC_CTRL0 0x4011 +#define ADAU1761_ALC_CTRL1 0x4012 +#define ADAU1761_ALC_CTRL2 0x4013 +#define ADAU1761_ALC_CTRL3 0x4014 #define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020 #define ADAU1761_PLAY_MIXER_LEFT0 0x401c #define ADAU1761_PLAY_MIXER_LEFT1 0x401d @@ -71,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU1761_REC_MIXER_RIGHT0, 0x00 }, { ADAU1761_REC_MIXER_RIGHT1, 0x00 }, { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_ALC_CTRL0, 0x00 }, + { ADAU1761_ALC_CTRL1, 0x00 }, + { ADAU1761_ALC_CTRL2, 0x00 }, + { ADAU1761_ALC_CTRL3, 0x00 }, { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT0, 0x00 }, @@ -121,6 +129,10 @@ static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1); static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1); static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1); +static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0); + static const unsigned int adau1761_bias_select_values[] = { 0, 2, 3, }; @@ -147,6 +159,103 @@ static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum, ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text, adau1761_bias_select_values); +static const unsigned int adau1761_pga_slew_time_values[] = { + 3, 0, 1, 2, +}; + +static const char * const adau1761_pga_slew_time_text[] = { + "Off", + "24 ms", + "48 ms", + "96 ms", +}; + +static const char * const adau1761_alc_function_text[] = { + "Off", + "Right", + "Left", + "Stereo", + "DSP control", +}; + +static const char * const adau1761_alc_hold_time_text[] = { + "2.67 ms", + "5.34 ms", + "10.68 ms", + "21.36 ms", + "42.72 ms", + "85.44 ms", + "170.88 ms", + "341.76 ms", + "683.52 ms", + "1367 ms", + "2734.1 ms", + "5468.2 ms", + "10936 ms", + "21873 ms", + "43745 ms", + "87491 ms", +}; + +static const char * const adau1761_alc_attack_time_text[] = { + "6 ms", + "12 ms", + "24 ms", + "48 ms", + "96 ms", + "192 ms", + "384 ms", + "768 ms", + "1540 ms", + "3070 ms", + "6140 ms", + "12290 ms", + "24580 ms", + "49150 ms", + "98300 ms", + "196610 ms", +}; + +static const char * const adau1761_alc_decay_time_text[] = { + "24 ms", + "48 ms", + "96 ms", + "192 ms", + "384 ms", + "768 ms", + "15400 ms", + "30700 ms", + "61400 ms", + "12290 ms", + "24580 ms", + "49150 ms", + "98300 ms", + "196610 ms", + "393220 ms", + "786430 ms", +}; + +static const char * const adau1761_alc_ng_type_text[] = { + "Hold", + "Mute", + "Fade", + "Fade + Mute", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum, + ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text, + adau1761_pga_slew_time_values); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum, + ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum, + ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum, + ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum, + ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum, + ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text); + static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = { SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT, 4, 1, 0), @@ -161,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = { SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1, ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv), + + SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum), + + SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0, + 3, 7, 0, adau1761_alc_max_gain_tlv), + SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum), + SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum), + SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1, + 0, 15, 0, adau1761_alc_target_tlv), + SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum), + SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum), + SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum), + SOC_SINGLE("ALC Capture Noise Gate Switch", + ADAU1761_ALC_CTRL3, 5, 1, 0), + SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume", + ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv), }; static const struct snd_kcontrol_new adau1761_single_mode_controls[] = { @@ -632,6 +757,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg) case ADAU1761_DEJITTER: case ADAU1761_CLK_ENABLE0: case ADAU1761_CLK_ENABLE1: + case ADAU1761_ALC_CTRL0: + case ADAU1761_ALC_CTRL1: + case ADAU1761_ALC_CTRL2: + case ADAU1761_ALC_CTRL3: return true; default: break; diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c new file mode 100644 index 000000000000..45a5d2dcc0f2 --- /dev/null +++ b/sound/soc/codecs/adau7118-hw.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw +// driver +// +// Copyright 2019 Analog Devices Inc. + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +#include "adau7118.h" + +static int adau7118_probe_hw(struct platform_device *pdev) +{ + return adau7118_probe(&pdev->dev, NULL, true); +} + +static const struct of_device_id adau7118_of_match[] = { + { .compatible = "adi,adau7118" }, + {} +}; +MODULE_DEVICE_TABLE(of, adau7118_of_match); + +static const struct platform_device_id adau7118_id[] = { + { .name = "adau7118" }, + { } +}; +MODULE_DEVICE_TABLE(platform, adau7118_id); + +static struct platform_driver adau7118_driver_hw = { + .driver = { + .name = "adau7118", + .of_match_table = adau7118_of_match, + }, + .probe = adau7118_probe_hw, + .id_table = adau7118_id, +}; +module_platform_driver(adau7118_driver_hw); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c new file mode 100644 index 000000000000..a8211362fe82 --- /dev/null +++ b/sound/soc/codecs/adau7118-i2c.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C +// +// Copyright 2019 Analog Devices Inc. + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adau7118.h" + +static const struct reg_default adau7118_reg_defaults[] = { + { ADAU7118_REG_VENDOR_ID, 0x41 }, + { ADAU7118_REG_DEVICE_ID1, 0x71 }, + { ADAU7118_REG_DEVICE_ID2, 0x18 }, + { ADAU7118_REG_REVISION_ID, 0x00 }, + { ADAU7118_REG_ENABLES, 0x3F }, + { ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 }, + { ADAU7118_REG_HPF_CONTROL, 0xD0 }, + { ADAU7118_REG_SPT_CTRL1, 0x41 }, + { ADAU7118_REG_SPT_CTRL2, 0x00 }, + { ADAU7118_REG_SPT_CX(0), 0x01 }, + { ADAU7118_REG_SPT_CX(1), 0x11 }, + { ADAU7118_REG_SPT_CX(2), 0x21 }, + { ADAU7118_REG_SPT_CX(3), 0x31 }, + { ADAU7118_REG_SPT_CX(4), 0x41 }, + { ADAU7118_REG_SPT_CX(5), 0x51 }, + { ADAU7118_REG_SPT_CX(6), 0x61 }, + { ADAU7118_REG_SPT_CX(7), 0x71 }, + { ADAU7118_REG_DRIVE_STRENGTH, 0x2a }, + { ADAU7118_REG_RESET, 0x00 }, +}; + +static const struct regmap_config adau7118_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = adau7118_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .max_register = ADAU7118_REG_RESET, +}; + +static int adau7118_probe_i2c(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *map; + + map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config); + if (IS_ERR(map)) { + dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map)); + return PTR_ERR(map); + } + + return adau7118_probe(&i2c->dev, map, false); +} + +static const struct of_device_id adau7118_of_match[] = { + { .compatible = "adi,adau7118" }, + {} +}; +MODULE_DEVICE_TABLE(of, adau7118_of_match); + +static const struct i2c_device_id adau7118_id[] = { + {"adau7118", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, adau7118_id); + +static struct i2c_driver adau7118_driver = { + .driver = { + .name = "adau7118", + .of_match_table = adau7118_of_match, + }, + .probe = adau7118_probe_i2c, + .id_table = adau7118_id, +}; +module_i2c_driver(adau7118_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c new file mode 100644 index 000000000000..841229dcbca1 --- /dev/null +++ b/sound/soc/codecs/adau7118.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver +// +// Copyright 2019 Analog Devices Inc. + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "adau7118.h" + +#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0) +#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x) +#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4) +#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4) +#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x) +#define ADAU7118_TRISTATE_MASK BIT(6) +#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x) +#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1) +#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x) +#define ADAU7118_SAI_MODE_MASK BIT(0) +#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x) +#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0) +#define ADAU7118_LRCLK_BCLK_POL(x) \ + FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x) +#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4) +#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x) +#define ADAU7118_FULL_SOFT_R_MASK BIT(1) +#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x) + +struct adau7118_data { + struct regmap *map; + struct device *dev; + struct regulator *iovdd; + struct regulator *dvdd; + u32 slot_width; + u32 slots; + bool hw_mode; + bool right_j; +}; + +/* Input Enable */ +static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = { + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = { + /* Input Enable Switches */ + SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[0]), + SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[1]), + SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[2]), + SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[3]), + + /* PDM Clocks */ + SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0), + + /* Output channels */ + SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7), + 0, 0), +}; + +static const struct snd_soc_dapm_route adau7118_routes_sw[] = { + { "PDM0", "Capture Switch", "PDM_DAT0" }, + { "PDM1", "Capture Switch", "PDM_DAT1" }, + { "PDM2", "Capture Switch", "PDM_DAT2" }, + { "PDM3", "Capture Switch", "PDM_DAT3" }, + { "AIF1TX1", NULL, "PDM0" }, + { "AIF1TX2", NULL, "PDM0" }, + { "AIF1TX3", NULL, "PDM1" }, + { "AIF1TX4", NULL, "PDM1" }, + { "AIF1TX5", NULL, "PDM2" }, + { "AIF1TX6", NULL, "PDM2" }, + { "AIF1TX7", NULL, "PDM3" }, + { "AIF1TX8", NULL, "PDM3" }, + { "Capture", NULL, "PDM_CLK0" }, + { "Capture", NULL, "PDM_CLK1" }, +}; + +static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = { + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route adau7118_routes_hw[] = { + { "AIF1TX", NULL, "PDM_DAT0" }, + { "AIF1TX", NULL, "PDM_DAT1" }, + { "AIF1TX", NULL, "PDM_DAT2" }, + { "AIF1TX", NULL, "PDM_DAT3" }, +}; + +static const struct snd_soc_dapm_widget adau7118_widgets[] = { + SND_SOC_DAPM_INPUT("PDM_DAT0"), + SND_SOC_DAPM_INPUT("PDM_DAT1"), + SND_SOC_DAPM_INPUT("PDM_DAT2"), + SND_SOC_DAPM_INPUT("PDM_DAT3"), +}; + +static int adau7118_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int chan, ret; + + dev_dbg(st->dev, "Set channel map, %d", tx_num); + + for (chan = 0; chan < tx_num; chan++) { + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CX(chan), + ADAU7118_SPT_SLOT_MASK, + ADAU7118_SPT_SLOT(tx_slot[chan])); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret = 0; + u32 regval; + + dev_dbg(st->dev, "Set format, fmt:%d\n", fmt); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + ADAU7118_DATA_FMT(0)); + break; + case SND_SOC_DAIFMT_LEFT_J: + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + ADAU7118_DATA_FMT(1)); + break; + case SND_SOC_DAIFMT_RIGHT_J: + st->right_j = true; + break; + default: + dev_err(st->dev, "Invalid format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + regval = ADAU7118_LRCLK_BCLK_POL(0); + break; + case SND_SOC_DAIFMT_NB_IF: + regval = ADAU7118_LRCLK_BCLK_POL(2); + break; + case SND_SOC_DAIFMT_IB_NF: + regval = ADAU7118_LRCLK_BCLK_POL(1); + break; + case SND_SOC_DAIFMT_IB_IF: + regval = ADAU7118_LRCLK_BCLK_POL(3); + break; + default: + dev_err(st->dev, "Invalid Inv mask %d", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL2, + ADAU7118_LRCLK_BCLK_POL_MASK, + regval); + if (ret < 0) + return ret; + + return 0; +} + +static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret; + + dev_dbg(st->dev, "Set tristate, %d\n", tristate); + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_TRISTATE_MASK, + ADAU7118_TRISTATE(tristate)); + if (ret < 0) + return ret; + + return 0; +} + +static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, + int slot_width) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret = 0; + u32 regval; + + dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width); + + switch (slot_width) { + case 32: + regval = ADAU7118_SLOT_WIDTH(0); + break; + case 24: + regval = ADAU7118_SLOT_WIDTH(2); + break; + case 16: + regval = ADAU7118_SLOT_WIDTH(1); + break; + default: + dev_err(st->dev, "Invalid slot width:%d\n", slot_width); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_SLOT_WIDTH_MASK, regval); + if (ret < 0) + return ret; + + st->slot_width = slot_width; + st->slots = slots; + + return 0; +} + +static int adau7118_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + u32 data_width = params_width(params), slots_width; + int ret; + u32 regval; + + if (!st->slots) { + /* set stereo mode */ + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_SAI_MODE_MASK, + ADAU7118_SAI_MODE(0)); + if (ret < 0) + return ret; + + slots_width = 32; + } else { + slots_width = st->slot_width; + } + + if (data_width > slots_width) { + dev_err(st->dev, "Invalid data_width:%d, slots_width:%d", + data_width, slots_width); + return -EINVAL; + } + + if (st->right_j) { + switch (slots_width - data_width) { + case 8: + /* delay bclck by 8 */ + regval = ADAU7118_DATA_FMT(2); + break; + case 12: + /* delay bclck by 12 */ + regval = ADAU7118_DATA_FMT(3); + break; + case 16: + /* delay bclck by 16 */ + regval = ADAU7118_DATA_FMT(4); + break; + default: + dev_err(st->dev, + "Cannot set right_j setting, slot_w:%d, data_w:%d\n", + slots_width, data_width); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + regval); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adau7118_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau7118_data *st = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(st->dev, "Set bias level %d\n", level); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_OFF) { + /* power on */ + ret = regulator_enable(st->iovdd); + if (ret) + return ret; + + /* there's no timing constraints before enabling dvdd */ + ret = regulator_enable(st->dvdd); + if (ret) { + regulator_disable(st->iovdd); + return ret; + } + + if (st->hw_mode) + return 0; + + regcache_cache_only(st->map, false); + /* sync cache */ + ret = snd_soc_component_cache_sync(component); + } + break; + case SND_SOC_BIAS_OFF: + /* power off */ + ret = regulator_disable(st->dvdd); + if (ret) + return ret; + + ret = regulator_disable(st->iovdd); + if (ret) + return ret; + + if (st->hw_mode) + return 0; + + /* cache only */ + regcache_mark_dirty(st->map); + regcache_cache_only(st->map, true); + + break; + } + + return ret; +} + +static int adau7118_component_probe(struct snd_soc_component *component) +{ + struct adau7118_data *st = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + int ret = 0; + + if (st->hw_mode) { + ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw, + ARRAY_SIZE(adau7118_widgets_hw)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw, + ARRAY_SIZE(adau7118_routes_hw)); + } else { + snd_soc_component_init_regmap(component, st->map); + ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw, + ARRAY_SIZE(adau7118_widgets_sw)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw, + ARRAY_SIZE(adau7118_routes_sw)); + } + + return ret; +} + +static const struct snd_soc_dai_ops adau7118_ops = { + .hw_params = adau7118_hw_params, + .set_channel_map = adau7118_set_channel_map, + .set_fmt = adau7118_set_fmt, + .set_tdm_slot = adau7118_set_tdm_slot, + .set_tristate = adau7118_set_tristate, +}; + +static struct snd_soc_dai_driver adau7118_dai = { + .name = "adau7118-hifi-capture", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 192000, + .sig_bits = 24, + }, +}; + +static const struct snd_soc_component_driver adau7118_component_driver = { + .probe = adau7118_component_probe, + .set_bias_level = adau7118_set_bias_level, + .dapm_widgets = adau7118_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets), + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static void adau7118_regulator_disable(void *data) +{ + struct adau7118_data *st = data; + int ret; + /* + * If we fail to disable DVDD, don't bother in trying IOVDD. We + * actually don't want to be left in the situation where DVDD + * is enabled and IOVDD is disabled. + */ + ret = regulator_disable(st->dvdd); + if (ret) + return; + + regulator_disable(st->iovdd); +} + +static int adau7118_regulator_setup(struct adau7118_data *st) +{ + st->iovdd = devm_regulator_get(st->dev, "iovdd"); + if (IS_ERR(st->iovdd)) { + dev_err(st->dev, "Could not get iovdd: %ld\n", + PTR_ERR(st->iovdd)); + return PTR_ERR(st->iovdd); + } + + st->dvdd = devm_regulator_get(st->dev, "dvdd"); + if (IS_ERR(st->dvdd)) { + dev_err(st->dev, "Could not get dvdd: %ld\n", + PTR_ERR(st->dvdd)); + return PTR_ERR(st->dvdd); + } + /* just assume the device is in reset */ + if (!st->hw_mode) { + regcache_mark_dirty(st->map); + regcache_cache_only(st->map, true); + } + + return devm_add_action_or_reset(st->dev, adau7118_regulator_disable, + st); +} + +static int adau7118_parset_dt(const struct adau7118_data *st) +{ + int ret; + u32 dec_ratio = 0; + /* 4 inputs */ + u32 clk_map[4], regval; + + if (st->hw_mode) + return 0; + + ret = device_property_read_u32(st->dev, "adi,decimation-ratio", + &dec_ratio); + if (!ret) { + switch (dec_ratio) { + case 64: + regval = ADAU7118_DEC_RATIO(0); + break; + case 32: + regval = ADAU7118_DEC_RATIO(1); + break; + case 16: + regval = ADAU7118_DEC_RATIO(2); + break; + default: + dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio); + return -EINVAL; + } + + ret = regmap_update_bits(st->map, + ADAU7118_REG_DEC_RATIO_CLK_MAP, + ADAU7118_DEC_RATIO_MASK, regval); + if (ret) + return ret; + } + + ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map", + clk_map, ARRAY_SIZE(clk_map)); + if (!ret) { + int pdm; + u32 _clk_map = 0; + + for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++) + _clk_map |= (clk_map[pdm] << (pdm + 4)); + + ret = regmap_update_bits(st->map, + ADAU7118_REG_DEC_RATIO_CLK_MAP, + ADAU7118_CLK_MAP_MASK, _clk_map); + if (ret) + return ret; + } + + return 0; +} + +int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode) +{ + struct adau7118_data *st; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->dev = dev; + st->hw_mode = hw_mode; + dev_set_drvdata(dev, st); + + if (!hw_mode) { + st->map = map; + adau7118_dai.ops = &adau7118_ops; + /* + * Perform a full soft reset. This will set all register's + * with their reset values. + */ + ret = regmap_update_bits(map, ADAU7118_REG_RESET, + ADAU7118_FULL_SOFT_R_MASK, + ADAU7118_FULL_SOFT_R(1)); + if (ret) + return ret; + } + + ret = adau7118_parset_dt(st); + if (ret) + return ret; + + ret = adau7118_regulator_setup(st); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, + &adau7118_component_driver, + &adau7118_dai, 1); +} +EXPORT_SYMBOL_GPL(adau7118_probe); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118.h b/sound/soc/codecs/adau7118.h new file mode 100644 index 000000000000..c65679a4dff1 --- /dev/null +++ b/sound/soc/codecs/adau7118.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ADAU7118_H +#define _LINUX_ADAU7118_H + +struct regmap; +struct device; + +/* register map */ +#define ADAU7118_REG_VENDOR_ID 0x00 +#define ADAU7118_REG_DEVICE_ID1 0x01 +#define ADAU7118_REG_DEVICE_ID2 0x02 +#define ADAU7118_REG_REVISION_ID 0x03 +#define ADAU7118_REG_ENABLES 0x04 +#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05 +#define ADAU7118_REG_HPF_CONTROL 0x06 +#define ADAU7118_REG_SPT_CTRL1 0x07 +#define ADAU7118_REG_SPT_CTRL2 0x08 +#define ADAU7118_REG_SPT_CX(num) (0x09 + (num)) +#define ADAU7118_REG_DRIVE_STRENGTH 0x11 +#define ADAU7118_REG_RESET 0x12 + +int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode); + +#endif diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 3c1bd24a1057..dd14caf9091a 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -1,15 +1,23 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for ChromeOS Embedded Controller codec. + * Copyright 2019 Google, Inc. + * + * ChromeOS Embedded Controller codec driver. * * This driver uses the cros-ec interface to communicate with the ChromeOS * EC for audio function. */ +#include <crypto/hash.h> +#include <crypto/sha.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/io.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> @@ -18,92 +26,279 @@ #include <sound/soc.h> #include <sound/tlv.h> -#define DRV_NAME "cros-ec-codec" - -/** - * struct cros_ec_codec_data - ChromeOS EC codec driver data. - * @dev: Device structure used in sysfs. - * @ec_device: cros_ec_device structure to talk to the physical device. - * @component: Pointer to the component. - * @max_dmic_gain: Maximum gain in dB supported by EC codec. - */ -struct cros_ec_codec_data { +struct cros_ec_codec_priv { struct device *dev; struct cros_ec_device *ec_device; - struct snd_soc_component *component; - unsigned int max_dmic_gain; + + /* common */ + uint32_t ec_capabilities; + + uint64_t ec_shm_addr; + uint32_t ec_shm_len; + + uint64_t ap_shm_phys_addr; + uint32_t ap_shm_len; + uint64_t ap_shm_addr; + uint64_t ap_shm_last_alloc; + + /* DMIC */ + atomic_t dmic_probed; + + /* WoV */ + bool wov_enabled; + uint8_t *wov_audio_shm_p; + uint32_t wov_audio_shm_len; + uint8_t wov_audio_shm_type; + uint8_t *wov_lang_shm_p; + uint32_t wov_lang_shm_len; + uint8_t wov_lang_shm_type; + + struct mutex wov_dma_lock; + uint8_t wov_buf[64000]; + uint32_t wov_rp, wov_wp; + size_t wov_dma_offset; + bool wov_burst_read; + struct snd_pcm_substream *wov_substream; + struct delayed_work wov_copy_work; + struct notifier_block wov_notifier; }; -static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); +static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap) +{ + return priv->ec_capabilities & BIT(cap); +} -static int ec_command_get_gain(struct snd_soc_component *component, - struct ec_param_codec_i2s *param, - struct ec_codec_i2s_gain *resp) +static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd, + uint8_t *out, size_t outsize, + uint8_t *in, size_t insize) { - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - max(sizeof(struct ec_param_codec_i2s), - sizeof(struct ec_codec_i2s_gain))]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; + struct cros_ec_command *msg; + + msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + if (!msg) + return -ENOMEM; msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = sizeof(struct ec_codec_i2s_gain); + msg->command = cmd; + msg->outsize = outsize; + msg->insize = insize; - memcpy(msg->data, param, msg->outsize); + if (outsize) + memcpy(msg->data, out, outsize); - ret = cros_ec_cmd_xfer_status(ec_device, msg); - if (ret > 0) - memcpy(resp, msg->data, msg->insize); + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto error; + + if (insize) + memcpy(in, msg->data, insize); + ret = 0; +error: + kfree(msg); return ret; } -/* - * Wrapper for EC command without response. - */ -static int ec_command_no_resp(struct snd_soc_component *component, - struct ec_param_codec_i2s *param) +static int calculate_sha256(struct cros_ec_codec_priv *priv, + uint8_t *buf, uint32_t size, uint8_t *digest) { - struct cros_ec_codec_data *codec_data = + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("sha256", CRYPTO_ALG_TYPE_SHASH, 0); + if (IS_ERR(tfm)) { + dev_err(priv->dev, "can't alloc shash\n"); + return PTR_ERR(tfm); + } + + { + SHASH_DESC_ON_STACK(desc, tfm); + + desc->tfm = tfm; + + crypto_shash_digest(desc, buf, size, digest); + shash_desc_zero(desc); + } + + crypto_free_shash(tfm); + +#ifdef DEBUG + { + char digest_str[65]; + + bin2hex(digest_str, digest, 32); + digest_str[64] = 0; + dev_dbg(priv->dev, "hash=%s\n", digest_str); + } +#endif + + return 0; +} + +static int dmic_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - struct cros_ec_device *ec_device = codec_data->ec_device; - u8 buffer[sizeof(struct cros_ec_command) + - sizeof(struct ec_param_codec_i2s)]; - struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_gain_idx r; + int ret; - msg->version = 0; - msg->command = EC_CMD_CODEC_I2S; - msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = 0; + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret; + ucontrol->value.integer.value[0] = r.gain; + + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret; + ucontrol->value.integer.value[1] = r.gain; + + return 0; +} + +static int dmic_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct soc_mixer_control *control = + (struct soc_mixer_control *)kcontrol->private_value; + int max_dmic_gain = control->max; + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + struct ec_param_ec_codec_dmic p; + int ret; + + if (left > max_dmic_gain || right > max_dmic_gain) + return -EINVAL; + + dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right); + + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; + p.set_gain_idx_param.gain = left; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret < 0) + return ret; + + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + p.set_gain_idx_param.gain = right; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0); + +enum { + DMIC_CTL_GAIN = 0, +}; + +static struct snd_kcontrol_new dmic_controls[] = { + [DMIC_CTL_GAIN] = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, + 0, 0, 0, dmic_get_gain, dmic_put_gain, + dmic_gain_tlv), +}; + +static int dmic_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + struct soc_mixer_control *control; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_max_gain r; + int ret; + + if (!atomic_add_unless(&priv->dmic_probed, 1, 1)) + return 0; + + p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) { + dev_warn(dev, "get_max_gain() unsupported\n"); + return 0; + } - memcpy(msg->data, param, msg->outsize); + dev_dbg(dev, "max gain = %d\n", r.max_gain); - return cros_ec_cmd_xfer_status(ec_device, msg); + control = (struct soc_mixer_control *) + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = r.max_gain; + control->platform_max = r.max_gain; + + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); } -static int set_i2s_config(struct snd_soc_component *component, - enum ec_i2s_config i2s_config) +static int i2s_rx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct ec_param_codec_i2s param; + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_sample_depth depth; + int ret; - dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, - i2s_config); + if (params_rate(params) != 48000) + return -EINVAL; - param.cmd = EC_CODEC_I2S_SET_CONFIG; - param.i2s_config = i2s_config; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24; + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "set depth to %u\n", depth); + + p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH; + p.set_sample_depth_param.depth = depth; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret < 0) + return ret; - return ec_command_no_resp(component, ¶m); + dev_dbg(component->dev, "set bclk to %u\n", + snd_soc_params_to_bclk(params)); + + p.cmd = EC_CODEC_I2S_RX_SET_BCLK; + p.set_bclk_param.bclk = snd_soc_params_to_bclk(params); + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); } -static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; - enum ec_i2s_config i2s_config; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_daifmt daifmt; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: @@ -121,300 +316,727 @@ static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - i2s_config = EC_DAI_FMT_I2S; + daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S; break; - case SND_SOC_DAIFMT_RIGHT_J: - i2s_config = EC_DAI_FMT_RIGHT_J; + daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J; break; - case SND_SOC_DAIFMT_LEFT_J: - i2s_config = EC_DAI_FMT_LEFT_J; + daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J; break; + default: + return -EINVAL; + } - case SND_SOC_DAIFMT_DSP_A: - i2s_config = EC_DAI_FMT_PCM_A; - break; + dev_dbg(component->dev, "set format to %u\n", daifmt); - case SND_SOC_DAIFMT_DSP_B: - i2s_config = EC_DAI_FMT_PCM_B; - break; + p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT; + p.set_daifmt_param.daifmt = daifmt; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, +}; +static int i2s_rx_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 cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, "enable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_ENABLE; + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, "disable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_DISABLE; + break; default: - return -EINVAL; + return 0; } - return set_i2s_config(component, i2s_config); + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = { + {"I2S RX", NULL, "DMIC"}, + {"I2S RX", NULL, "I2S RX Enable"}, +}; + +static struct snd_soc_dai_driver i2s_rx_dai_driver = { + .name = "EC Codec I2S RX", + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &i2s_rx_dai_ops, +}; + +static int i2s_rx_probe(struct snd_soc_component *component) +{ + return dmic_probe(component); } -static int set_i2s_sample_depth(struct snd_soc_component *component, - enum ec_sample_depth_value depth) +static const struct snd_soc_component_driver i2s_rx_component_driver = { + .probe = i2s_rx_probe, + .dapm_widgets = i2s_rx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets), + .dapm_routes = i2s_rx_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), +}; + +static void *wov_map_shm(struct cros_ec_codec_priv *priv, + uint8_t shm_id, uint32_t *len, uint8_t *type) { - struct ec_param_codec_i2s param; + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_shm_addr r; + uint32_t req, offset; + + p.cmd = EC_CODEC_GET_SHM_ADDR; + p.get_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n"); + return NULL; + } - dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); + dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len); + + *len = r.len; + *type = r.type; + + switch (r.type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + return (void __force *)devm_ioremap_wc(priv->dev, + r.phys_addr + priv->ec_shm_addr, r.len); + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + if (r.phys_addr) { + dev_err(priv->dev, "unknown status\n"); + return NULL; + } + + req = round_up(r.len, PAGE_SIZE); + dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req); + + if (priv->ap_shm_last_alloc + req > + priv->ap_shm_phys_addr + priv->ap_shm_len) { + dev_err(priv->dev, "insufficient space for AP SHM\n"); + return NULL; + } + + dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n", + priv->ap_shm_last_alloc, req); + + p.cmd = EC_CODEC_SET_SHM_ADDR; + p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc; + p.set_shm_addr_param.len = req; + p.set_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + NULL, 0) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n"); + return NULL; + } + + /* + * Note: EC codec only requests for `r.len' but we allocate + * round up PAGE_SIZE `req'. + */ + offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr; + priv->ap_shm_last_alloc += req; + + return (void *)(uintptr_t)(priv->ap_shm_addr + offset); + default: + return NULL; + } +} - param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; - param.depth = depth; +static bool wov_queue_full(struct cros_ec_codec_priv *priv) +{ + return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp; +} - return ec_command_no_resp(component, ¶m); +static size_t wov_queue_size(struct cros_ec_codec_priv *priv) +{ + if (priv->wov_wp >= priv->wov_rp) + return priv->wov_wp - priv->wov_rp; + else + return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp; } -static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) +static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len) { - struct ec_param_codec_i2s param; + struct snd_pcm_runtime *runtime = priv->wov_substream->runtime; + size_t req; + + while (len) { + req = min(len, runtime->dma_bytes - priv->wov_dma_offset); + if (priv->wov_wp >= priv->wov_rp) + req = min(req, (size_t)priv->wov_wp - priv->wov_rp); + else + req = min(req, sizeof(priv->wov_buf) - priv->wov_rp); + + memcpy(runtime->dma_area + priv->wov_dma_offset, + priv->wov_buf + priv->wov_rp, req); - dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); + priv->wov_dma_offset += req; + if (priv->wov_dma_offset == runtime->dma_bytes) + priv->wov_dma_offset = 0; - param.cmd = EC_CODEC_I2S_SET_BCLK; - param.bclk = bclk; + priv->wov_rp += req; + if (priv->wov_rp == sizeof(priv->wov_buf)) + priv->wov_rp = 0; - return ec_command_no_resp(component, ¶m); + len -= req; + } + + snd_pcm_period_elapsed(priv->wov_substream); } -static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv) { - struct snd_soc_component *component = dai->component; - unsigned int rate, bclk; - int ret; + size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); - rate = params_rate(params); - if (rate != 48000) - return -EINVAL; + while (period_bytes && wov_queue_size(priv) >= period_bytes) { + wov_queue_dequeue(priv, period_bytes); + period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); + } +} - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); - break; - case SNDRV_PCM_FORMAT_S24_LE: - ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); - break; - default: - return -EINVAL; +static void wov_queue_enqueue(struct cros_ec_codec_priv *priv, + uint8_t *addr, size_t len, bool iomem) +{ + size_t req; + + while (len) { + if (wov_queue_full(priv)) { + wov_queue_try_dequeue(priv); + + if (wov_queue_full(priv)) { + dev_err(priv->dev, "overrun detected\n"); + return; + } + } + + if (priv->wov_wp >= priv->wov_rp) + req = sizeof(priv->wov_buf) - priv->wov_wp; + else + /* Note: waste 1-byte to differentiate full and empty */ + req = priv->wov_rp - priv->wov_wp - 1; + req = min(req, len); + + if (iomem) + memcpy_fromio(priv->wov_buf + priv->wov_wp, + (void __force __iomem *)addr, req); + else + memcpy(priv->wov_buf + priv->wov_wp, addr, req); + + priv->wov_wp += req; + if (priv->wov_wp == sizeof(priv->wov_buf)) + priv->wov_wp = 0; + + addr += req; + len -= req; } - if (ret < 0) + + wov_queue_try_dequeue(priv); +} + +static int wov_read_audio_shm(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio_shm r; + int ret; + + p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n"); return ret; + } - bclk = snd_soc_params_to_bclk(params); - return set_i2s_bclk(component, bclk); + if (!r.len) + dev_dbg(priv->dev, "no data, sleep\n"); + else + wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len, + priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM); + return -EAGAIN; } -static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { - .hw_params = cros_ec_i2s_hw_params, - .set_fmt = cros_ec_i2s_set_dai_fmt, -}; +static int wov_read_audio(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio r; + int remain = priv->wov_burst_read ? 16000 : 320; + int ret; -static struct snd_soc_dai_driver cros_ec_dai[] = { - { - .name = "cros_ec_codec I2S", - .id = 0, - .capture = { - .stream_name = "I2S Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &cros_ec_i2s_dai_ops, + while (remain >= 0) { + p.cmd = EC_CODEC_WOV_READ_AUDIO; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, + "failed to EC_CODEC_WOV_READ_AUDIO\n"); + return ret; + } + + if (!r.len) { + dev_dbg(priv->dev, "no data, sleep\n"); + priv->wov_burst_read = false; + break; + } + + wov_queue_enqueue(priv, r.buf, r.len, false); + remain -= r.len; } -}; -static int get_ec_mic_gain(struct snd_soc_component *component, - u8 *left, u8 *right) + return -EAGAIN; +} + +static void wov_copy_work(struct work_struct *w) { - struct ec_param_codec_i2s param; - struct ec_codec_i2s_gain resp; + struct cros_ec_codec_priv *priv = + container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); int ret; - param.cmd = EC_CODEC_GET_GAIN; + mutex_lock(&priv->wov_dma_lock); + if (!priv->wov_substream) { + dev_warn(priv->dev, "no pcm substream\n"); + goto leave; + } - ret = ec_command_get_gain(component, ¶m, &resp); - if (ret < 0) - return ret; + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) + ret = wov_read_audio_shm(priv); + else + ret = wov_read_audio(priv); + + if (ret == -EAGAIN) + schedule_delayed_work(&priv->wov_copy_work, + msecs_to_jiffies(10)); + else if (ret) + dev_err(priv->dev, "failed to read audio data\n"); +leave: + mutex_unlock(&priv->wov_dma_lock); +} - *left = resp.left; - *right = resp.right; +static int wov_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + ucontrol->value.integer.value[0] = priv->wov_enabled; return 0; } -static int mic_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int wov_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - u8 left, right; + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + struct ec_param_ec_codec_wov p; int ret; - ret = get_ec_mic_gain(component, &left, &right); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = left; - ucontrol->value.integer.value[1] = right; + if (priv->wov_enabled != enabled) { + if (enabled) + p.cmd = EC_CODEC_WOV_ENABLE; + else + p.cmd = EC_CODEC_WOV_DISABLE; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to %s wov\n", + enabled ? "enable" : "disable"); + return ret; + } + + priv->wov_enabled = enabled; + } return 0; } -static int set_ec_mic_gain(struct snd_soc_component *component, - u8 left, u8 right) +static int wov_set_lang_shm(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) { - struct ec_param_codec_i2s param; + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param; + int ret; - dev_dbg(component->dev, "%s set mic gain to %u, %u\n", - __func__, left, right); + if (size > priv->wov_lang_shm_len) { + dev_err(priv->dev, "no enough SHM size: %d\n", + priv->wov_lang_shm_len); + return -EIO; + } - param.cmd = EC_CODEC_SET_GAIN; - param.gain.left = left; - param.gain.right = right; + switch (priv->wov_lang_shm_type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p, + buf, size); + memset_io((void __force __iomem *)priv->wov_lang_shm_p + size, + 0, priv->wov_lang_shm_len - size); + break; + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + memcpy(priv->wov_lang_shm_p, buf, size); + memset(priv->wov_lang_shm_p + size, 0, + priv->wov_lang_shm_len - size); - return ec_command_no_resp(component, ¶m); + /* make sure write to memory before calling host command */ + wmb(); + break; + } + + p.cmd = EC_CODEC_WOV_SET_LANG_SHM; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n"); + return ret; + } + + return 0; } -static int mic_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int wov_set_lang(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) { - struct snd_soc_component *component = - snd_soc_kcontrol_component(kcontrol); - struct cros_ec_codec_data *codec_data = - snd_soc_component_get_drvdata(component); - int left = ucontrol->value.integer.value[0]; - int right = ucontrol->value.integer.value[1]; - unsigned int max_dmic_gain = codec_data->max_dmic_gain; + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param; + size_t i, req; + int ret; - if (left > max_dmic_gain || right > max_dmic_gain) - return -EINVAL; + for (i = 0; i < size; i += req) { + req = min(size - i, ARRAY_SIZE(pp->buf)); + + p.cmd = EC_CODEC_WOV_SET_LANG; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + pp->offset = i; + memcpy(pp->buf, buf + i, req); + pp->len = req; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n"); + return ret; + } + } - return set_ec_mic_gain(component, (u8)left, (u8)right); + return 0; } -static struct snd_kcontrol_new mic_gain_control = - SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, - mic_gain_get, mic_gain_put, ec_mic_gain_tlv); - -static int enable_i2s(struct snd_soc_component *component, int enable) +static int wov_hotword_model_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *bytes, + unsigned int size) { - struct ec_param_codec_i2s param; + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_get_lang r; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t *buf; + int ret; + + /* Skips the TLV header. */ + bytes += 2; + size -= 8; + + dev_dbg(priv->dev, "%s: size=%d\n", __func__, size); + + buf = memdup_user(bytes, size); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = calculate_sha256(priv, buf, size, digest); + if (ret) + goto leave; - dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); + p.cmd = EC_CODEC_WOV_GET_LANG; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) + goto leave; + + if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) { + dev_dbg(priv->dev, "not updated"); + goto leave; + } - param.cmd = EC_CODEC_I2S_ENABLE; - param.i2s_enable = enable; + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) + ret = wov_set_lang_shm(priv, buf, size, digest); + else + ret = wov_set_lang(priv, buf, size, digest); - return ec_command_no_resp(component, ¶m); +leave: + kfree(buf); + return ret; } -static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static struct snd_kcontrol_new wov_controls[] = { + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0, + wov_enable_get, wov_enable_put), + SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL, + wov_hotword_model_put), +}; + +static struct snd_soc_dai_driver wov_dai_driver = { + .name = "Wake on Voice", + .capture = { + .stream_name = "WoV Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static int wov_host_event(struct notifier_block *nb, + unsigned long queued_during_suspend, void *notify) { - struct snd_soc_component *component = - snd_soc_dapm_to_component(w->dapm); + struct cros_ec_codec_priv *priv = + container_of(nb, struct cros_ec_codec_priv, wov_notifier); + u32 host_event; + + dev_dbg(priv->dev, "%s\n", __func__); + + host_event = cros_ec_get_host_event(priv->ec_device); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) { + schedule_delayed_work(&priv->wov_copy_work, 0); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); - return enable_i2s(component, 1); +static int wov_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + int ret; - case SND_SOC_DAPM_PRE_PMD: - dev_dbg(component->dev, - "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); - return enable_i2s(component, 0); + mutex_init(&priv->wov_dma_lock); + INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work); + + priv->wov_notifier.notifier_call = wov_host_event; + ret = blocking_notifier_chain_register( + &priv->ec_device->event_notifier, &priv->wov_notifier); + if (ret) + return ret; + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) { + priv->wov_lang_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_LANG, + &priv->wov_lang_shm_len, + &priv->wov_lang_shm_type); + if (!priv->wov_lang_shm_p) + return -EFAULT; } - return 0; + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) { + priv->wov_audio_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_AUDIO, + &priv->wov_audio_shm_len, + &priv->wov_audio_shm_type); + if (!priv->wov_audio_shm_p) + return -EFAULT; + } + + return dmic_probe(component); } -/* - * The goal of this DAPM route is to turn on/off I2S using EC - * host command when capture stream is started/stopped. - */ -static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("DMIC"), +static void wov_remove(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); - /* - * Control EC to enable/disable I2S. - */ - SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, - 0, 0, cros_ec_i2s_enable_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + blocking_notifier_chain_unregister( + &priv->ec_device->event_notifier, &priv->wov_notifier); +} - SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), -}; +static int wov_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hw_param = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_16000, + .channels_min = 1, + .channels_max = 1, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = 0x20000 / 8, + .periods_min = 8, + .periods_max = 8, + .buffer_bytes_max = 0x20000, + }; + + return snd_soc_set_runtime_hwparams(substream, &hw_param); +} -static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { - { "I2STX", NULL, "DMIC" }, - { "I2STX", NULL, "I2S Enable" }, -}; +static int wov_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); -/* - * Read maximum gain from device property and set it to mixer control. - */ -static int cros_ec_set_gain_range(struct device *dev) + mutex_lock(&priv->wov_dma_lock); + priv->wov_substream = substream; + priv->wov_rp = priv->wov_wp = 0; + priv->wov_dma_offset = 0; + priv->wov_burst_read = true; + mutex_unlock(&priv->wov_dma_lock); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int wov_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct soc_mixer_control *control; - struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); - int rc; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); - rc = device_property_read_u32(dev, "max-dmic-gain", - &codec_data->max_dmic_gain); - if (rc) - return rc; + mutex_lock(&priv->wov_dma_lock); + wov_queue_dequeue(priv, wov_queue_size(priv)); + priv->wov_substream = NULL; + mutex_unlock(&priv->wov_dma_lock); - control = (struct soc_mixer_control *) - mic_gain_control.private_value; - control->max = codec_data->max_dmic_gain; - control->platform_max = codec_data->max_dmic_gain; + cancel_delayed_work_sync(&priv->wov_copy_work); - return 0; + return snd_pcm_lib_free_vmalloc_buffer(substream); } -static int cros_ec_codec_probe(struct snd_soc_component *component) +static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - int rc; - - struct cros_ec_codec_data *codec_data = + struct snd_pcm_runtime *runtime = substream->runtime; + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(component); - rc = cros_ec_set_gain_range(codec_data->dev); - if (rc) - return rc; + return bytes_to_frames(runtime, priv->wov_dma_offset); +} - return snd_soc_add_component_controls(component, &mic_gain_control, 1); +static struct page *wov_pcm_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_lib_get_vmalloc_page(substream, offset); } -static const struct snd_soc_component_driver cros_ec_component_driver = { - .probe = cros_ec_codec_probe, - .dapm_widgets = cros_ec_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), - .dapm_routes = cros_ec_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), +static const struct snd_soc_component_driver wov_component_driver = { + .probe = wov_probe, + .remove = wov_remove, + .controls = wov_controls, + .num_controls = ARRAY_SIZE(wov_controls), + .open = wov_pcm_open, + .hw_params = wov_pcm_hw_params, + .hw_free = wov_pcm_hw_free, + .pointer = wov_pcm_pointer, + .page = wov_pcm_page, }; -/* - * Platform device and platform driver fro cros-ec-codec. - */ -static int cros_ec_codec_platform_probe(struct platform_device *pd) +static int cros_ec_codec_platform_probe(struct platform_device *pdev) { - struct device *dev = &pd->dev; - struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); - struct cros_ec_codec_data *codec_data; + struct device *dev = &pdev->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_codec_priv *priv; + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_capabilities r; + int ret; +#ifdef CONFIG_OF + struct device_node *node; + struct resource res; + u64 ec_shm_size; + const __be32 *regaddr_p; +#endif - codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), - GFP_KERNEL); - if (!codec_data) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - codec_data->dev = dev; - codec_data->ec_device = ec_device; +#ifdef CONFIG_OF + regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL); + if (regaddr_p) { + priv->ec_shm_addr = of_read_number(regaddr_p, 2); + priv->ec_shm_len = ec_shm_size; - platform_set_drvdata(pd, codec_data); + dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n", + priv->ec_shm_addr, priv->ec_shm_len); + } + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (node) { + ret = of_address_to_resource(node, 0, &res); + if (!ret) { + priv->ap_shm_phys_addr = res.start; + priv->ap_shm_len = resource_size(&res); + priv->ap_shm_addr = + (uint64_t)(uintptr_t)devm_ioremap_wc( + dev, priv->ap_shm_phys_addr, + priv->ap_shm_len); + priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; + + dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", + priv->ap_shm_phys_addr, priv->ap_shm_len); + } + } +#endif + + priv->dev = dev; + priv->ec_device = ec_device; + atomic_set(&priv->dmic_probed, 0); + + p.cmd = EC_CODEC_GET_CAPABILITIES; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n"); + return ret; + } + priv->ec_capabilities = r.capabilities; + + platform_set_drvdata(pdev, priv); + + ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver, + &i2s_rx_dai_driver, 1); + if (ret) + return ret; - return devm_snd_soc_register_component(dev, &cros_ec_component_driver, - cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); + return devm_snd_soc_register_component(dev, &wov_component_driver, + &wov_dai_driver, 1); } #ifdef CONFIG_OF @@ -427,7 +1049,7 @@ MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match); static struct platform_driver cros_ec_codec_platform_driver = { .driver = { - .name = DRV_NAME, + .name = "cros-ec-codec", .of_match_table = of_match_ptr(cros_ec_codec_of_match), }, .probe = cros_ec_codec_platform_probe, @@ -438,4 +1060,4 @@ module_platform_driver(cros_ec_codec_platform_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ChromeOS EC codec driver"); MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>"); -MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS("platform:cros-ec-codec"); diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 1c1ba7bea4d8..2ad00ed21bec 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1507,7 +1507,7 @@ static int cx2072x_probe(struct snd_soc_component *codec) regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init, ARRAY_SIZE(cx2072x_reg_init)); - /* configre PortC as input device */ + /* configure PortC as input device */ regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL, 0x20, 0x20); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 4570f662fb48..6803d39e09a5 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -14,13 +14,11 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/hdaudio_ext.h> +#include <sound/hda_i915.h> #include <sound/hda_codec.h> #include <sound/hda_register.h> -#include "hdac_hda.h" -#define HDAC_ANALOG_DAI_ID 0 -#define HDAC_DIGITAL_DAI_ID 1 -#define HDAC_ALT_ANALOG_DAI_ID 2 +#include "hdac_hda.h" #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_U8 | \ @@ -32,6 +30,11 @@ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) +#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + static int hdac_hda_dai_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static void hdac_hda_dai_close(struct snd_pcm_substream *substream, @@ -121,7 +124,46 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = { .formats = STUB_FORMATS, .sig_bits = 24, }, -} +}, +{ + .id = HDAC_HDMI_0_DAI_ID, + .name = "intel-hdmi-hifi1", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi1", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_HDMI_1_DAI_ID, + .name = "intel-hdmi-hifi2", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi2", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_HDMI_2_DAI_ID, + .name = "intel-hdmi-hifi3", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi3", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, }; @@ -135,10 +177,11 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, hda_pvt = snd_soc_component_get_drvdata(component); pcm = &hda_pvt->pcm[dai->id]; + if (tx_mask) - pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; else - pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; return 0; } @@ -278,6 +321,12 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct hda_pcm *cpcm; const char *pcm_name; + /* + * map DAI ID to the closest matching PCM name, using the naming + * scheme used by hda-codec snd_hda_gen_build_pcms() and for + * HDMI in hda_codec patch_hdmi.c) + */ + switch (dai->id) { case HDAC_ANALOG_DAI_ID: pcm_name = "Analog"; @@ -288,13 +337,22 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, case HDAC_ALT_ANALOG_DAI_ID: pcm_name = "Alt Analog"; break; + case HDAC_HDMI_0_DAI_ID: + pcm_name = "HDMI 0"; + break; + case HDAC_HDMI_1_DAI_ID: + pcm_name = "HDMI 1"; + break; + case HDAC_HDMI_2_DAI_ID: + pcm_name = "HDMI 2"; + break; default: dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id); return NULL; } list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { - if (strpbrk(cpcm->name, pcm_name)) + if (strstr(cpcm->name, pcm_name)) return cpcm; } @@ -302,6 +360,18 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, return NULL; } +static bool is_hdmi_codec(struct hda_codec *hcodec) +{ + struct hda_pcm *cpcm; + + list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { + if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI) + return true; + } + + return false; +} + static int hdac_hda_codec_probe(struct snd_soc_component *component) { struct hdac_hda_priv *hda_pvt = @@ -322,6 +392,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_hdac_ext_bus_link_get(hdev->bus, hlink); + /* + * Ensure any HDA display is powered at codec probe. + * After snd_hda_codec_device_new(), display power is + * managed by runtime PM. + */ + if (hda_pvt->need_display_power) + snd_hdac_display_power(hdev->bus, + HDA_CODEC_IDX_CONTROLLER, true); + ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, hdev->addr, hcodec); if (ret < 0) { @@ -366,20 +445,31 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) dev_dbg(&hdev->dev, "no patch file found\n"); } + /* configure codec for 1:1 PCM:DAI mapping */ + hcodec->mst_no_extra_pcms = 1; + ret = snd_hda_codec_parse_pcms(hcodec); if (ret < 0) { dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); goto error; } - ret = snd_hda_codec_build_controls(hcodec); - if (ret < 0) { - dev_err(&hdev->dev, "unable to create controls %d\n", ret); - goto error; + /* HDMI controls need to be created in machine drivers */ + if (!is_hdmi_codec(hcodec)) { + ret = snd_hda_codec_build_controls(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", + ret); + goto error; + } } hcodec->core.lazy_cache = true; + if (hda_pvt->need_display_power) + snd_hdac_display_power(hdev->bus, + HDA_CODEC_IDX_CONTROLLER, false); + /* * hdac_device core already sets the state to active and calls * get_noresume. So enable runtime and set the device to suspend. diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h index 6b1bd4f428e7..e145cec085b8 100644 --- a/sound/soc/codecs/hdac_hda.h +++ b/sound/soc/codecs/hdac_hda.h @@ -6,6 +6,16 @@ #ifndef __HDAC_HDA_H__ #define __HDAC_HDA_H__ +enum { + HDAC_ANALOG_DAI_ID = 0, + HDAC_DIGITAL_DAI_ID, + HDAC_ALT_ANALOG_DAI_ID, + HDAC_HDMI_0_DAI_ID, + HDAC_HDMI_1_DAI_ID, + HDAC_HDMI_2_DAI_ID, + HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID, +}; + struct hdac_hda_pcm { int stream_tag[2]; unsigned int format_val[2]; @@ -13,7 +23,8 @@ struct hdac_hda_pcm { struct hdac_hda_priv { struct hda_codec codec; - struct hdac_hda_pcm pcm[2]; + struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID]; + bool need_display_power; }; #define hdac_to_hda_priv(_hdac) \ diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h index 1f3e8e230cf2..6d8938a3fb64 100644 --- a/sound/soc/codecs/madera.h +++ b/sound/soc/codecs/madera.h @@ -27,6 +27,7 @@ #define MADERA_FLL_SRC_NONE -1 #define MADERA_FLL_SRC_MCLK1 0 #define MADERA_FLL_SRC_MCLK2 1 +#define MADERA_FLL_SRC_MCLK3 2 #define MADERA_FLL_SRC_SLIMCLK 3 #define MADERA_FLL_SRC_FLL1 4 #define MADERA_FLL_SRC_FLL2 5 @@ -51,6 +52,7 @@ #define MADERA_CLK_SRC_MCLK1 0x0 #define MADERA_CLK_SRC_MCLK2 0x1 +#define MADERA_CLK_SRC_MCLK3 0x2 #define MADERA_CLK_SRC_FLL1 0x4 #define MADERA_CLK_SRC_FLL2 0x5 #define MADERA_CLK_SRC_FLL3 0x6 diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index e3d311fb510e..f53235be77d9 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -228,6 +228,10 @@ #define CDC_A_RX_EAR_CTL (0xf19E) #define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0) #define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0) +#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6) +#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6) +#define RX_EAR_CTL_PA_SEL_MASK BIT(7) +#define RX_EAR_CTL_PA_SEL BIT(7) #define CDC_A_SPKR_DAC_CTL (0xf1B0) #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) @@ -312,6 +316,7 @@ static const char *const hph_text[] = { "ZERO", "Switch", }; static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT( ARRAY_SIZE(hph_text), hph_text); +static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum); static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum); static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum); @@ -685,6 +690,34 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w, return 0; } +static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_EAR_PA_EN_MASK, + RX_EAR_CTL_PA_EAR_PA_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0); + /* Delay to reduce ear turn off pop */ + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_SEL_MASK, 0); + break; + } + return 0; +} + static const struct reg_default wcd_reg_defaults_2_0[] = { {CDC_A_RX_COM_OCP_CTL, 0xD1}, {CDC_A_RX_COM_OCP_COUNT, 0xFF}, @@ -801,12 +834,20 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { {"PDM_TX", NULL, "A_MCLK2"}, {"A_MCLK2", NULL, "A_MCLK"}, + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + /* Headset (RX MIX1 and RX MIX2) */ {"HEADPHONE", NULL, "HPHL PA"}, {"HEADPHONE", NULL, "HPHR PA"}, - {"HPHL PA", NULL, "EAR_HPHL_CLK"}, - {"HPHR PA", NULL, "EAR_HPHR_CLK"}, + {"HPHL DAC", NULL, "EAR_HPHL_CLK"}, + {"HPHR DAC", NULL, "EAR_HPHR_CLK"}, {"CP", NULL, "NCP_CLK"}, @@ -847,11 +888,20 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AMIC1"), SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("HEADPHONE"), /* RX stuff */ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, + 0, 0, NULL, 0, + pm8916_wcd_analog_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), + SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index bb737fd678cc..1b830ea4f6ed 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -93,6 +93,8 @@ struct mt6358_priv { int mtkaif_protocol; struct regulator *avdd_reg; + + int wov_enabled; }; int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, @@ -464,6 +466,106 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol, return ret; } +static void mt6358_restore_pga(struct mt6358_priv *priv); + +static int mt6358_enable_wov_phase2(struct mt6358_priv *priv) +{ + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0800); + mt6358_restore_pga(priv); + + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0025); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0005); + + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0451); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1); + + return 0; +} + +static int mt6358_disable_wov_phase2(struct mt6358_priv *priv) +{ + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0450); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0c00); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8); + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0004); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0000); + mt6358_restore_pga(priv); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0010); + + return 0; +} + +static int mt6358_get_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->wov_enabled; + return 0; +} + +static int mt6358_put_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + + if (priv->wov_enabled != enabled) { + if (enabled) + mt6358_enable_wov_phase2(priv); + else + mt6358_disable_wov_phase2(priv); + + priv->wov_enabled = enabled; + } + + return 0; +} + static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); @@ -483,6 +585,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = { MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1, 8, 4, 0, snd_soc_get_volsw, mt6358_put_volsw, pga_tlv), + + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0, + mt6358_get_wov, mt6358_put_wov), }; /* MUX */ diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 88b75695fbf7..9711fab296eb 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -9,7 +9,9 @@ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -59,9 +61,11 @@ struct pcm3168a_priv { struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES]; struct regmap *regmap; struct clk *scki; + struct gpio_desc *gpio_rst; unsigned long sysclk; struct pcm3168a_io_params io_params[2]; + struct snd_soc_dai_driver dai_drv[2]; }; static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" }; @@ -314,6 +318,34 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, return 0; } +static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE; + unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6; + + if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) { + /* S16_LE is only supported in RIGHT_J mode */ + formats |= SNDRV_PCM_FMTBIT_S16_LE; + + /* + * If multi DIN/DOUT is not selected, RIGHT_J can only support + * two channels (no TDM support) + */ + if (pcm3168a->io_params[dai->id].tdm_slots != 2) + channel_max = 2; + } + + if (dai->id == PCM3168A_DAI_DAC) { + dai->driver->playback.channels_max = channel_max; + dai->driver->playback.formats = formats; + } else { + dai->driver->capture.channels_max = channel_max; + dai->driver->capture.formats = formats; + } +} + static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) { struct snd_soc_component *component = dai->component; @@ -376,6 +408,8 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); + pcm3168a_update_fixup_pcm_stream(dai); + return 0; } @@ -409,6 +443,8 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, else io_params->tdm_mask = rx_mask; + pcm3168a_update_fixup_pcm_stream(dai); + return 0; } @@ -530,63 +566,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return 0; } -static int pcm3168a_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); - unsigned int sample_min; - unsigned int channel_max; - unsigned int channel_maxs[] = { - 8, /* DAC */ - 6 /* ADC */ - }; - - /* - * Available Data Bits - * - * RIGHT_J : 24 / 16 - * LEFT_J : 24 - * I2S : 24 - * - * TDM available - * - * I2S - * LEFT_J - */ - switch (pcm3168a->io_params[dai->id].fmt) { - case PCM3168A_FMT_RIGHT_J: - sample_min = 16; - channel_max = 2; - break; - case PCM3168A_FMT_LEFT_J: - case PCM3168A_FMT_I2S: - case PCM3168A_FMT_DSP_A: - case PCM3168A_FMT_DSP_B: - sample_min = 24; - channel_max = channel_maxs[dai->id]; - break; - default: - sample_min = 24; - channel_max = 2; - } - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - sample_min, 32); - - /* Allow all channels in multi DIN/DOUT mode */ - if (pcm3168a->io_params[dai->id].tdm_slots == 2) - channel_max = channel_maxs[dai->id]; - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 2, channel_max); - - return 0; -} static const struct snd_soc_dai_ops pcm3168a_dai_ops = { - .startup = pcm3168a_startup, .set_fmt = pcm3168a_set_dai_fmt, .set_sysclk = pcm3168a_set_dai_sysclk, .hw_params = pcm3168a_hw_params, @@ -666,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg) static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case PCM3168A_RST_SMODE: case PCM3168A_DAC_ZERO: case PCM3168A_ADC_OV: return true; @@ -725,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, pcm3168a); + /* + * Request the reset (connected to RST pin) gpio line as non exclusive + * as the same reset line might be connected to multiple pcm3168a codec + * + * The RST is low active, we want the GPIO line to be high initially, so + * request the initial level to LOW which in practice means DEASSERTED: + * The deasserted level of GPIO_ACTIVE_LOW is HIGH. + */ + pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(pcm3168a->gpio_rst)) { + ret = PTR_ERR(pcm3168a->gpio_rst); + if (ret != -EPROBE_DEFER ) + dev_err(dev, "failed to acquire RST gpio: %d\n", ret); + + return ret; + } + pcm3168a->scki = devm_clk_get(dev, "scki"); if (IS_ERR(pcm3168a->scki)) { ret = PTR_ERR(pcm3168a->scki); @@ -766,18 +766,28 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) goto err_regulator; } - ret = pcm3168a_reset(pcm3168a); - if (ret) { - dev_err(dev, "Failed to reset device: %d\n", ret); - goto err_regulator; + if (pcm3168a->gpio_rst) { + /* + * The device is taken out from reset via GPIO line, wait for + * 3846 SCKI clock cycles for the internal reset de-assertion + */ + msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk)); + } else { + ret = pcm3168a_reset(pcm3168a); + if (ret) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_regulator; + } } pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_idle(dev); - ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, pcm3168a_dais, - ARRAY_SIZE(pcm3168a_dais)); + memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv)); + ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, + pcm3168a->dai_drv, + ARRAY_SIZE(pcm3168a->dai_drv)); if (ret) { dev_err(dev, "failed to register component: %d\n", ret); goto err_regulator; @@ -806,6 +816,15 @@ static void pcm3168a_disable(struct device *dev) void pcm3168a_remove(struct device *dev) { + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + /* + * The RST is low active, we want the GPIO line to be low when the + * driver is removed, so set level to 1 which in practice means + * ASSERTED: + * The asserted level of GPIO_ACTIVE_LOW is LOW. + */ + gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1); pm_runtime_disable(dev); #ifndef CONFIG_PM pcm3168a_disable(dev); diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index be1e276e3631..2552073e54ce 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -61,7 +61,6 @@ static const struct reg_sequence init_list[] = { { RT1011_DAC_SET_1, 0xe702 }, { RT1011_DAC_SET_3, 0x2004 }, }; -#define RT1011_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt1011_reg[] = { {0x0000, 0x0000}, @@ -684,7 +683,8 @@ static int rt1011_reg_init(struct snd_soc_component *component) { struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); - regmap_multi_reg_write(rt1011->regmap, init_list, RT1011_INIT_REG_LEN); + regmap_multi_reg_write(rt1011->regmap, + init_list, ARRAY_SIZE(init_list)); return 0; } @@ -989,7 +989,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5, static const char * const rt1011_tdm_data_out_select[] = { "TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR", - "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS", + "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS", "SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST" }; @@ -1002,7 +1002,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12, rt1011_tdm_l_ch_data_select); static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum, - RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select); + RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select); static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0, rt1011_tdm_l_ch_data_select); @@ -1024,9 +1024,9 @@ static const char * const rt1011_tdm_adc_swap_select[] = { "L/R", "R/L", "L/L", "R/R" }; -static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6, +static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6, rt1011_tdm_adc_swap_select); -static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4, +static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4, rt1011_tdm_adc_swap_select); static void rt1011_reset(struct regmap *regmap) @@ -1092,9 +1092,9 @@ static bool rt1011_validate_bq_drc_coeff(unsigned short reg) { if ((reg == RT1011_DAC_SET_1) | (reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) | - (reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) | + (reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) | (reg == RT1011_MIXER_1) | - (reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 && + (reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 && reg <= RT1011_POWER_8) | (reg == RT1011_CLASS_D_POS) | (reg == RT1011_ANALOG_CTRL) | (reg >= RT1011_SPK_TEMP_PROTECT_0 && @@ -1163,9 +1163,6 @@ static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol, (struct rt1011_bq_drc_params *)ucontrol->value.integer.value; unsigned int i, mode_idx = 0; - if (!component->card->instantiated) - return 0; - if (strstr(ucontrol->id.name, "AdvanceMode Initial Set")) mode_idx = RT1011_ADVMODE_INITIAL_SET; else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff")) @@ -1236,9 +1233,6 @@ static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); - if (!component->card->instantiated) - return 0; - rt1011->cali_done = 0; if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && ucontrol->value.integer.value[0]) @@ -1284,9 +1278,6 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.integer.value[0] == rt1011->r0_reg) return 0; - if (!component->card->instantiated) - return 0; - if (ucontrol->value.integer.value[0] == 0) return -EINVAL; @@ -1298,7 +1289,7 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol, r0_integer = format / rt1011->r0_reg / 128; r0_factor = ((format / rt1011->r0_reg * 100) / 128) - (r0_integer * 100); - dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n", + dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n", r0_integer, r0_factor, rt1011->r0_reg); if (rt1011->r0_reg) @@ -1640,6 +1631,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; default: ret = -EINVAL; + goto _set_fmt_err_; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -1650,6 +1642,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; default: ret = -EINVAL; + goto _set_fmt_err_; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -1666,6 +1659,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; default: ret = -EINVAL; + goto _set_fmt_err_; } switch (dai->id) { @@ -1683,6 +1677,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) ret = -EINVAL; } +_set_fmt_err_: snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -1778,7 +1773,8 @@ static int rt1011_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); return ret; } @@ -1805,8 +1801,8 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai, struct snd_soc_component *component = dai->component; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - unsigned int val = 0, tdm_en = 0; - int ret = 0; + unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum; + int ret = 0, first_bit, last_bit; snd_soc_dapm_mutex_lock(dapm); if (rx_mask || tx_mask) @@ -1829,6 +1825,7 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai, break; default: ret = -EINVAL; + goto _set_tdm_err_; } switch (slot_width) { @@ -1848,22 +1845,153 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai, break; default: ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + first_bit = find_next_bit((unsigned long *)&rx_mask, 32, 0); + if (rx_slotnum > 1 || rx_slotnum == 0) { + ret = -EINVAL; + dev_dbg(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK, + RT1011_MONO_L_CHANNEL); + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_4, + RT1011_TDM_I2S_TX_L_DAC1_1_MASK | + RT1011_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + snd_soc_component_update_bits(component, + RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK, + RT1011_MONO_R_CHANNEL); + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_4, + RT1011_TDM_I2S_TX_L_DAC1_1_MASK | + RT1011_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + first_bit = find_next_bit((unsigned long *)&tx_mask, 32, 0); + last_bit = find_last_bit((unsigned long *)&tx_mask, 32); + if (tx_slotnum > 2 || (last_bit-first_bit) > 1) { + ret = -EINVAL; + dev_dbg(component->dev, "too many tx slots or tx slot location error\n"); + goto _set_tdm_err_; + } + + if (tx_slotnum == 1) { + snd_soc_component_update_bits(component, RT1011_TDM1_SET_2, + RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK | + RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit); + switch (first_bit) { + case 1: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC1_1_MASK, + RT1011_TDM_I2S_RX_ADC1_1_LL); + break; + case 3: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC2_1_MASK, + RT1011_TDM_I2S_RX_ADC2_1_LL); + break; + case 5: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC3_1_MASK, + RT1011_TDM_I2S_RX_ADC3_1_LL); + break; + case 7: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC4_1_MASK, + RT1011_TDM_I2S_RX_ADC4_1_LL); + break; + case 0: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC1_1_MASK, 0); + break; + case 2: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC2_1_MASK, 0); + break; + case 4: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC3_1_MASK, 0); + break; + case 6: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC4_1_MASK, 0); + break; + default: + ret = -EINVAL; + dev_dbg(component->dev, + "tx slot location error\n"); + goto _set_tdm_err_; + } + } else if (tx_slotnum == 2) { + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_2, + RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK | + RT1011_TDM_ADCDAT1_DATA_LOCATION, + RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit); + break; + default: + ret = -EINVAL; + dev_dbg(component->dev, + "tx slot location should be paired and start from slot0/2/4/6\n"); + goto _set_tdm_err_; + } } snd_soc_component_update_bits(component, RT1011_TDM1_SET_1, RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK | - RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); + RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); snd_soc_component_update_bits(component, RT1011_TDM2_SET_1, RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK | - RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); + RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); snd_soc_component_update_bits(component, RT1011_TDM1_SET_2, - RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en); + RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en); snd_soc_component_update_bits(component, RT1011_TDM2_SET_2, - RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en); - snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET, - RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG, - RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT); + RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en); + if (tx_slotnum) + snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET, + RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG, + RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT); +_set_tdm_err_: snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -1982,7 +2110,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = { .remove = rt1011_remove, .suspend = rt1011_suspend, .resume = rt1011_resume, - .set_bias_level = rt1011_set_bias_level, + .set_bias_level = rt1011_set_bias_level, .controls = rt1011_snd_controls, .num_controls = ARRAY_SIZE(rt1011_snd_controls), .dapm_widgets = rt1011_dapm_widgets, @@ -1991,9 +2119,9 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = { .num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes), .set_sysclk = rt1011_set_component_sysclk, .set_pll = rt1011_set_component_pll, - .use_pmdown_time = 1, - .endianness = 1, - .non_legacy_dai_naming = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, }; static const struct regmap_config rt1011_regmap = { @@ -2095,17 +2223,17 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) dc_offset = value << 16; regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value); dc_offset |= (value & 0xffff); - dev_info(dev, "ADC offset=0x%x\n", dc_offset); + dev_info(dev, "ADC offset=0x%x\n", dc_offset); regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value); dc_offset = value << 16; regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value); dc_offset |= (value & 0xffff); - dev_info(dev, "Gain0 offset=0x%x\n", dc_offset); + dev_info(dev, "Gain0 offset=0x%x\n", dc_offset); regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value); dc_offset = value << 16; regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value); dc_offset |= (value & 0xffff); - dev_info(dev, "Gain1 offset=0x%x\n", dc_offset); + dev_info(dev, "Gain1 offset=0x%x\n", dc_offset); if (cali_flag) { @@ -2125,7 +2253,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) while (count < chk_cnt) { msleep(100); regmap_read(rt1011->regmap, - RT1011_INIT_RECIPROCAL_SYN_24_16, &value); + RT1011_INIT_RECIPROCAL_SYN_24_16, &value); r0[count%3] = value << 16; regmap_read(rt1011->regmap, RT1011_INIT_RECIPROCAL_SYN_15_0, &value); @@ -2140,7 +2268,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) break; } if (count > chk_cnt) { - dev_err(dev, "Calibrate R0 Failure\n"); + dev_err(dev, "Calibrate R0 Failure\n"); ret = -EAGAIN; } else { format = 2147483648U; /* 2^24 * 128 */ @@ -2149,7 +2277,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) - (r0_integer * 100); rt1011->r0_reg = r0[0]; rt1011->cali_done = 1; - dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n", + dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n", r0_integer, r0_factor, r0[0]); } } @@ -2196,8 +2324,12 @@ static void rt1011_calibration_work(struct work_struct *work) struct rt1011_priv *rt1011 = container_of(work, struct rt1011_priv, cali_work); struct snd_soc_component *component = rt1011->component; + unsigned int r0_integer, r0_factor, format; - rt1011_calibrate(rt1011, 1); + if (rt1011->r0_calib) + rt1011_calibrate(rt1011, 0); + else + rt1011_calibrate(rt1011, 1); /* * This flag should reset after booting. @@ -2208,6 +2340,40 @@ static void rt1011_calibration_work(struct work_struct *work) /* initial */ rt1011_reg_init(component); + + /* Apply temperature and calibration data from device property */ + if (rt1011->temperature_calib <= 0xff && + rt1011->temperature_calib > 0) { + snd_soc_component_update_bits(component, + RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff, + (rt1011->temperature_calib << 2)); + } + + if (rt1011->r0_calib) { + rt1011->r0_reg = rt1011->r0_calib; + + format = 2147483648U; /* 2^24 * 128 */ + r0_integer = format / rt1011->r0_reg / 128; + r0_factor = ((format / rt1011->r0_reg * 100) / 128) + - (r0_integer * 100); + dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n", + r0_integer, r0_factor, rt1011->r0_reg); + + rt1011_r0_load(rt1011); + } +} + +static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev) +{ + device_property_read_u32(dev, "realtek,temperature_calib", + &rt1011->temperature_calib); + device_property_read_u32(dev, "realtek,r0_calib", + &rt1011->r0_calib); + + dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x", + __func__, rt1011->r0_calib, rt1011->temperature_calib); + + return 0; } static int rt1011_i2c_probe(struct i2c_client *i2c, @@ -2219,11 +2385,13 @@ static int rt1011_i2c_probe(struct i2c_client *i2c, rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv), GFP_KERNEL); - if (rt1011 == NULL) + if (!rt1011) return -ENOMEM; i2c_set_clientdata(i2c, rt1011); + rt1011_parse_dp(rt1011, &i2c->dev); + rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap); if (IS_ERR(rt1011->regmap)) { ret = PTR_ERR(rt1011->regmap); @@ -2254,7 +2422,6 @@ static void rt1011_i2c_shutdown(struct i2c_client *client) rt1011_reset(rt1011->regmap); } - static struct i2c_driver rt1011_i2c_driver = { .driver = { .name = "rt1011", diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h index 2d65983f3d0f..68fadc15fa8c 100644 --- a/sound/soc/codecs/rt1011.h +++ b/sound/soc/codecs/rt1011.h @@ -460,6 +460,23 @@ #define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3) #define RT1011_TDM_I2S_DOCK_EN_1_SFT 3 #define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3) +#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0) + +/* TDM1 Setting-3 (0x0118) */ +#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6) +#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4) +#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2) +#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0) +#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6) +#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4) +#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2) +#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0) + +/* TDM1 Setting-4 (0x011a) */ +#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12) +#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8) +#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12 +#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8 /* TDM2 Setting-2 (0x0120) */ #define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13) @@ -585,6 +602,12 @@ #define RT1011_STP_T0_EN_BIT 6 #define RT1011_STP_T0_EN (0x1 << 6) +/* Cross Biquad Setting-1 (0x0702) */ +#define RT1011_MONO_LR_SEL_MASK (0x3 << 5) +#define RT1011_MONO_L_CHANNEL (0x0 << 5) +#define RT1011_MONO_R_CHANNEL (0x1 << 5) +#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5) + /* ClassD Internal Setting-1 (0x1300) */ #define RT1011_DRIVER_READY_SPK (0x1 << 12) #define RT1011_DRIVER_READY_SPK_BIT 12 @@ -667,6 +690,7 @@ struct rt1011_priv { int bq_drc_set; unsigned int r0_reg, cali_done; + unsigned int r0_calib, temperature_calib; int recv_spk_mode; }; diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 892ea406a69b..57ff5aee452d 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -201,18 +201,18 @@ static irqreturn_t rt5514_spi_irq(int irq, void *data) } /* PCM for streaming audio from the DSP buffer */ -static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream) +static int rt5514_spi_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware); return 0; } -static int rt5514_spi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int rt5514_spi_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); int ret; @@ -234,10 +234,9 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream, return ret; } -static int rt5514_spi_hw_free(struct snd_pcm_substream *substream) +static int rt5514_spi_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); @@ -251,24 +250,22 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream) } static snd_pcm_uframes_t rt5514_spi_pcm_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct rt5514_dsp *rt5514_dsp = snd_soc_component_get_drvdata(component); return bytes_to_frames(runtime, rt5514_dsp->dma_offset); } -static const struct snd_pcm_ops rt5514_spi_pcm_ops = { - .open = rt5514_spi_pcm_open, - .hw_params = rt5514_spi_hw_params, - .hw_free = rt5514_spi_hw_free, - .pointer = rt5514_spi_pcm_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; +static struct page *rt5514_spi_pcm_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_lib_get_vmalloc_page(substream, offset); +} static int rt5514_spi_pcm_probe(struct snd_soc_component *component) { @@ -302,9 +299,13 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver rt5514_spi_component = { - .name = DRV_NAME, - .probe = rt5514_spi_pcm_probe, - .ops = &rt5514_spi_pcm_ops, + .name = DRV_NAME, + .probe = rt5514_spi_pcm_probe, + .open = rt5514_spi_pcm_open, + .hw_params = rt5514_spi_hw_params, + .hw_free = rt5514_spi_hw_free, + .pointer = rt5514_spi_pcm_pointer, + .page = rt5514_spi_pcm_page, }; /** diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 19662ee330d6..92d67010aeed 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3639,6 +3639,12 @@ static const struct rt5645_platform_data lattepanda_board_platform_data = { .inv_jd1_1 = true }; +static const struct rt5645_platform_data kahlee_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + static const struct dmi_system_id dmi_platform_data[] = { { .ident = "Chrome Buddy", @@ -3745,6 +3751,13 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&lattepanda_board_platform_data, }, + { + .ident = "Chrome Kahlee", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"), + }, + .driver_data = (void *)&kahlee_platform_data, + }, { } }; diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 2943692f66ed..e6c1ec6c426e 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3644,7 +3644,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1, RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK, RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); - break; + break; case CODEC_VER_0: regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC, RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); @@ -3663,7 +3663,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5663->regmap, RT5663_TDM_2, RT5663_DATA_SWAP_ADCDAT1_MASK, RT5663_DATA_SWAP_ADCDAT1_LL); - break; + break; default: dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); } diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index d681488f5312..25e28be3722e 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -24,6 +24,9 @@ #include <linux/firmware.h> #include <linux/acpi.h> +#include <sound/soc.h> + +#include "rt5677.h" #include "rt5677-spi.h" #define DRV_NAME "rt5677spi" @@ -45,9 +48,368 @@ #define RT5677_SPI_WRITE_16 0x1 #define RT5677_SPI_READ_16 0x0 +#define RT5677_BUF_BYTES_TOTAL 0x20000 +#define RT5677_MIC_BUF_ADDR 0x60030000 +#define RT5677_MODEL_ADDR 0x5FFC9800 +#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \ + sizeof(u32))) +#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000 + static struct spi_device *g_spi; static DEFINE_MUTEX(spi_mutex); +struct rt5677_dsp { + struct device *dev; + struct delayed_work copy_work; + struct mutex dma_lock; + struct snd_pcm_substream *substream; + size_t dma_offset; /* zero-based offset into runtime->dma_area */ + size_t avail_bytes; /* number of new bytes since last period */ + u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */ + bool new_hotword; /* a new hotword is fired */ +}; + +static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8, + .periods_min = 8, + .periods_max = 8, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL, +}; + +static struct snd_soc_dai_driver rt5677_spi_dai = { + /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name + * registered with ASoC is the name of the device "spi-RT5677AA:00", + * because we only have one DAI. See snd_soc_register_dais(). + */ + .name = "rt5677-dsp-cpu-dai", + .id = 0, + .capture = { + .stream_name = "DSP Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +/* PCM for streaming audio from the DSP buffer */ +static int rt5677_spi_pcm_open( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware); + return 0; +} + +static int rt5677_spi_pcm_close( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *codec_component = + snd_soc_rtdcom_lookup(rtd, "rt5677"); + struct rt5677_priv *rt5677 = + snd_soc_component_get_drvdata(codec_component); + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt5677_dsp->copy_work); + rt5677->set_dsp_vad(codec_component, false); + return 0; +} + +static int rt5677_spi_hw_params( + struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&rt5677_dsp->dma_lock); + ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + rt5677_dsp->substream = substream; + mutex_unlock(&rt5677_dsp->dma_lock); + + return ret; +} + +static int rt5677_spi_hw_free( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + mutex_lock(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = NULL; + mutex_unlock(&rt5677_dsp->dma_lock); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int rt5677_spi_prepare( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *rt5677_component = + snd_soc_rtdcom_lookup(rtd, "rt5677"); + struct rt5677_priv *rt5677 = + snd_soc_component_get_drvdata(rt5677_component); + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + rt5677->set_dsp_vad(rt5677_component, true); + rt5677_dsp->dma_offset = 0; + rt5677_dsp->avail_bytes = 0; + return 0; +} + +static snd_pcm_uframes_t rt5677_spi_pcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + return bytes_to_frames(runtime, rt5677_dsp->dma_offset); +} + +static int rt5677_spi_mic_write_offset(u32 *mic_write_offset) +{ + int ret; + /* Grab the first 4 bytes that hold the write pointer on the + * dsp, and check to make sure that it points somewhere inside the + * buffer. + */ + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset, + sizeof(u32)); + if (ret) + return ret; + /* Adjust the offset so that it's zero-based */ + *mic_write_offset = *mic_write_offset - sizeof(u32); + return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT; +} + +/* + * Copy one contiguous block of audio samples from the DSP mic buffer to the + * dma_area of the pcm runtime. The receiving buffer may wrap around. + * @begin: start offset of the block to copy, in bytes. + * @end: offset of the first byte after the block to copy, must be greater + * than or equal to begin. + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp, + u32 begin, u32 end) +{ + struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime; + size_t bytes_per_frame = frames_to_bytes(runtime, 1); + size_t first_chunk_len, second_chunk_len; + int ret; + + if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) { + dev_err(rt5677_dsp->dev, + "Invalid copy from (%u, %u), dma_area size %zu\n", + begin, end, runtime->dma_bytes); + return -EINVAL; + } + + /* The block to copy is empty */ + if (begin == end) + return 0; + + /* If the incoming chunk is too big for the receiving buffer, only the + * last "receiving buffer size - one frame" bytes are copied. + */ + if (end - begin > runtime->dma_bytes - bytes_per_frame) + begin = end - (runtime->dma_bytes - bytes_per_frame); + + /* May need to split to two chunks, calculate the size of each */ + first_chunk_len = end - begin; + second_chunk_len = 0; + if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) { + /* Receiving buffer wrapped around */ + second_chunk_len = first_chunk_len; + first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset; + second_chunk_len -= first_chunk_len; + } + + /* Copy first chunk */ + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin, + runtime->dma_area + rt5677_dsp->dma_offset, + first_chunk_len); + if (ret) + return ret; + rt5677_dsp->dma_offset += first_chunk_len; + if (rt5677_dsp->dma_offset == runtime->dma_bytes) + rt5677_dsp->dma_offset = 0; + + /* Copy second chunk */ + if (second_chunk_len) { + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + + begin + first_chunk_len, runtime->dma_area, + second_chunk_len); + if (!ret) + rt5677_dsp->dma_offset = second_chunk_len; + } + return ret; +} + +/* + * Copy a given amount of audio samples from the DSP mic buffer starting at + * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may + * wrap around. mic_read_offset is updated after successful copy. + * @amount: amount of samples to copy, in bytes. + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount) +{ + int ret = 0; + u32 target; + + if (amount == 0) + return ret; + + target = rt5677_dsp->mic_read_offset + amount; + /* Copy the first chunk in DSP's mic buffer */ + ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset, + min(target, RT5677_MIC_BUF_BYTES)); + + if (target >= RT5677_MIC_BUF_BYTES) { + /* Wrap around, copy the second chunk */ + target -= RT5677_MIC_BUF_BYTES; + ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target); + } + + if (!ret) + rt5677_dsp->mic_read_offset = target; + return ret; +} + +/* + * A delayed work that streams audio samples from the DSP mic buffer to the + * dma_area of the pcm runtime via SPI. + */ +static void rt5677_spi_copy_work(struct work_struct *work) +{ + struct rt5677_dsp *rt5677_dsp = + container_of(work, struct rt5677_dsp, copy_work.work); + struct snd_pcm_runtime *runtime; + u32 mic_write_offset; + size_t new_bytes, copy_bytes, period_bytes; + unsigned int delay; + int ret = 0; + + /* Ensure runtime->dma_area buffer does not go away while copying. */ + mutex_lock(&rt5677_dsp->dma_lock); + if (!rt5677_dsp->substream) { + dev_err(rt5677_dsp->dev, "No pcm substream\n"); + goto done; + } + + runtime = rt5677_dsp->substream->runtime; + + if (rt5677_spi_mic_write_offset(&mic_write_offset)) { + dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); + goto done; + } + + /* If this is the first time that we've asked for streaming data after + * a hotword is fired, we should start reading from the previous 2 + * seconds of audio from wherever the mic_write_offset is currently. + */ + if (rt5677_dsp->new_hotword) { + rt5677_dsp->new_hotword = false; + /* See if buffer wraparound happens */ + if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE) + rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES - + (RT5677_MIC_BUF_FIRST_READ_SIZE - + mic_write_offset); + else + rt5677_dsp->mic_read_offset = mic_write_offset - + RT5677_MIC_BUF_FIRST_READ_SIZE; + } + + /* Calculate the amount of new samples in bytes */ + if (rt5677_dsp->mic_read_offset <= mic_write_offset) + new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset; + else + new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset + - rt5677_dsp->mic_read_offset; + + /* Copy all new samples from DSP mic buffer, one period at a time */ + period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream); + while (new_bytes) { + copy_bytes = min(new_bytes, period_bytes + - rt5677_dsp->avail_bytes); + ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); + if (ret) { + dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); + goto done; + } + rt5677_dsp->avail_bytes += copy_bytes; + if (rt5677_dsp->avail_bytes >= period_bytes) { + snd_pcm_period_elapsed(rt5677_dsp->substream); + rt5677_dsp->avail_bytes = 0; + } + new_bytes -= copy_bytes; + } + + delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000); + schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay)); +done: + mutex_unlock(&rt5677_dsp->dma_lock); +} + +static struct page *rt5677_spi_pcm_page( + struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_lib_get_vmalloc_page(substream, offset); +} + +static int rt5677_spi_pcm_probe(struct snd_soc_component *component) +{ + struct rt5677_dsp *rt5677_dsp; + + rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp), + GFP_KERNEL); + if (!rt5677_dsp) + return -ENOMEM; + rt5677_dsp->dev = &g_spi->dev; + mutex_init(&rt5677_dsp->dma_lock); + INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work); + + snd_soc_component_set_drvdata(component, rt5677_dsp); + return 0; +} + +static const struct snd_soc_component_driver rt5677_spi_dai_component = { + .name = DRV_NAME, + .probe = rt5677_spi_pcm_probe, + .open = rt5677_spi_pcm_open, + .close = rt5677_spi_pcm_close, + .hw_params = rt5677_spi_hw_params, + .hw_free = rt5677_spi_hw_free, + .prepare = rt5677_spi_prepare, + .pointer = rt5677_spi_pcm_pointer, + .page = rt5677_spi_pcm_page, +}; + /* Select a suitable transfer command for the next transfer to ensure * the transfer address is always naturally aligned while minimizing * the total number of transfers required. @@ -218,9 +580,45 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) } EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware); +void rt5677_spi_hotword_detected(void) +{ + struct rt5677_dsp *rt5677_dsp; + + if (!g_spi) + return; + + rt5677_dsp = dev_get_drvdata(&g_spi->dev); + if (!rt5677_dsp) { + dev_err(&g_spi->dev, "Can't get rt5677_dsp\n"); + return; + } + + mutex_lock(&rt5677_dsp->dma_lock); + dev_info(rt5677_dsp->dev, "Hotword detected\n"); + rt5677_dsp->new_hotword = true; + mutex_unlock(&rt5677_dsp->dma_lock); + + schedule_delayed_work(&rt5677_dsp->copy_work, 0); +} +EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected); + static int rt5677_spi_probe(struct spi_device *spi) { + int ret; + g_spi = spi; + + ret = snd_soc_register_component(&spi->dev, &rt5677_spi_dai_component, + &rt5677_spi_dai, 1); + if (ret < 0) + dev_err(&spi->dev, "Failed to register component.\n"); + + return ret; +} + +static int rt5677_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_component(&spi->dev); return 0; } @@ -236,6 +634,7 @@ static struct spi_driver rt5677_spi_driver = { .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id), }, .probe = rt5677_spi_probe, + .remove = rt5677_spi_remove, }; module_spi_driver(rt5677_spi_driver); diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h index 6ba3369dc235..3af36ec928e9 100644 --- a/sound/soc/codecs/rt5677-spi.h +++ b/sound/soc/codecs/rt5677-spi.h @@ -12,5 +12,6 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len); int rt5677_spi_write(u32 addr, const void *txbuf, size_t len); int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw); +void rt5677_spi_hotword_detected(void); #endif /* __RT5677_SPI_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 315a3d39bc09..e9a051a50ab2 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -38,6 +38,10 @@ #define RT5677_DEVICE_ID 0x6327 +/* Register controlling boot vector */ +#define RT5677_DSP_BOOT_VECTOR 0x1801f090 +#define RT5677_MODEL_ADDR 0x5FFC9800 + #define RT5677_PR_RANGE_BASE (0xff + 1) #define RT5677_PR_SPACING 0x100 @@ -298,6 +302,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg) case RT5677_I2C_MASTER_CTRL7: case RT5677_I2C_MASTER_CTRL8: case RT5677_HAP_GENE_CTRL2: + case RT5677_PWR_ANLG2: /* Modified by DSP firmware */ case RT5677_PWR_DSP_ST: case RT5677_PRIV_DATA: case RT5677_ASRC_22: @@ -308,6 +313,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg) case RT5677_IRQ_CTRL1: case RT5677_IRQ_CTRL2: case RT5677_GPIO_ST: + case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */ + case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */ case RT5677_DSP_INB1_SRC_CTRL4: case RT5677_DSP_INB2_SRC_CTRL4: case RT5677_DSP_INB3_SRC_CTRL4: @@ -686,10 +693,8 @@ static int rt5677_dsp_mode_i2c_read( return ret; } -static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on) +static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on) { - struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); - if (on) { regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, RT5677_PWR_DSP, RT5677_PWR_DSP); @@ -701,86 +706,259 @@ static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on) } } +static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt5677->component); + /* Force dapm to sync before we enable the + * DSP to prevent write corruption + */ + snd_soc_dapm_sync(dapm); + + /* DMIC1 power = enabled + * DMIC CLK = 256 * fs / 12 + */ + regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1, + RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT); + + /* I2S pre divide 2 = /6 (clk_sys2) */ + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6); + + /* DSP Clock = MCLK1 (bypassed PLL2) */ + regmap_write(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_DSP_CLK_SRC_BYPASS); + + /* SAD Threshold1 */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f); + /* SAD Threshold2 */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5); + /* SAD Sample Rate Converter = Up 6 (8K to 48K) + * SAD Output Sample Rate = Same as I2S + * SAD Threshold3 + */ + regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4, + RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK | + RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT); + /* Minimum frame level within a pre-determined duration = 32 frames + * Bypass ADPCM Encoder/Decoder = Bypass ADPCM + * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable + * SAD Buffer Over-Writing = enable + * SAD Buffer Pop Mode Control = disable + * SAD Buffer Push Mode Control = enable + * SAD Detector Control = enable + * SAD Function Control = enable + * SAD Function Reset = normal + */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, + RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE | + RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH | + RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC | + RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT); + + /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it + * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save + * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack + * Detection" for more info. + */ + + /* Private register, no doc */ + regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4, + 0x0f00, 0x0100); + + /* LDO2 output = 1.2V + * LDO1 output = 1.2V (LDO_IN = 1.8V) + */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK, + 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT); + + /* Codec core power = power on + * LDO1 power = power on + */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CORE | RT5677_PWR_LDO1, + RT5677_PWR_CORE | RT5677_PWR_LDO1); + + /* Isolation for DCVDD4 = normal (set during probe) + * Isolation for DCVDD2 = normal (set during probe) + * Isolation for DSP = normal + * Isolation for Band 0~7 = disable + * Isolation for InBound 4~10 and OutBound 4~10 = disable + */ + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, + RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO | + RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO | + RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO | + RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO | + RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO | + RT5677_PWR_MLT_ISO); + + /* System Band 0~7 = power on + * InBound 4~10 and OutBound 4~10 = power on + * DSP = power on + * DSP CPU = stop (will be set to "run" after firmware loaded) + */ + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_SR7 | RT5677_PWR_SR6 | + RT5677_PWR_SR5 | RT5677_PWR_SR4 | + RT5677_PWR_SR3 | RT5677_PWR_SR2 | + RT5677_PWR_SR1 | RT5677_PWR_SR0 | + RT5677_PWR_MLT | RT5677_PWR_DSP | + RT5677_PWR_DSP_CPU); + + return 0; +} + +static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, + unsigned int len) +{ + struct snd_soc_component *component = rt5677->component; + Elf32_Ehdr *elf_hdr; + Elf32_Phdr *pr_hdr; + Elf32_Half i; + int ret = 0; + + if (!buf || (len < sizeof(Elf32_Ehdr))) + return -ENOMEM; + + elf_hdr = (Elf32_Ehdr *)buf; +#ifndef EM_XTENSA +#define EM_XTENSA 94 +#endif + if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1)) + dev_err(component->dev, "Wrong ELF header prefix\n"); + if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr)) + dev_err(component->dev, "Wrong Elf header size\n"); + if (elf_hdr->e_machine != EM_XTENSA) + dev_err(component->dev, "Wrong DSP code file\n"); + + if (len < elf_hdr->e_phoff) + return -ENOMEM; + pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff); + for (i = 0; i < elf_hdr->e_phnum; i++) { + /* TODO: handle p_memsz != p_filesz */ + if (pr_hdr->p_paddr && pr_hdr->p_filesz) { + dev_info(component->dev, "Load 0x%x bytes to 0x%x\n", + pr_hdr->p_filesz, pr_hdr->p_paddr); + + ret = rt5677_spi_write(pr_hdr->p_paddr, + buf + pr_hdr->p_offset, + pr_hdr->p_filesz); + if (ret) + dev_err(component->dev, "Load firmware failed %d\n", + ret); + } + pr_hdr++; + } + return ret; +} + +static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) +{ + const struct firmware *fwp; + struct device *dev = rt5677->component->dev; + int ret = 0; + + /* Load dsp firmware from rt5677_elf_vad file */ + ret = request_firmware(&fwp, "rt5677_elf_vad", dev); + if (ret) { + dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); + return ret; + } + dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); + + ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); + release_firmware(fwp); + return ret; +} + static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) { struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); - static bool activity; - int ret; + rt5677->dsp_vad_en_request = on; + rt5677->dsp_vad_en = on; if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI)) return -ENXIO; - if (on && !activity) { + schedule_delayed_work(&rt5677->dsp_work, 0); + return 0; +} + +static void rt5677_dsp_work(struct work_struct *work) +{ + struct rt5677_priv *rt5677 = + container_of(work, struct rt5677_priv, dsp_work.work); + static bool activity; + bool enable = rt5677->dsp_vad_en; + int i, val; + + + dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n", + enable, activity); + + if (enable && !activity) { activity = true; - regcache_cache_only(rt5677->regmap, false); - regcache_cache_bypass(rt5677->regmap, true); + /* Before a hotword is detected, GPIO1 pin is configured as IRQ + * output so that jack detect works. When a hotword is detected, + * the DSP firmware configures the GPIO1 pin as GPIO1 and + * drives a 1. rt5677_irq() is called after a rising edge on + * the GPIO1 pin, due to either jack detect event or hotword + * event, or both. All possible events are checked and handled + * in rt5677_irq() where GPIO1 pin is configured back to IRQ + * output if a hotword is detected. + */ - regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1); - regmap_update_bits(rt5677->regmap, - RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00); - regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, - RT5677_LDO1_SEL_MASK, 0x0); - regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, - RT5677_PWR_LDO1, RT5677_PWR_LDO1); - switch (rt5677->type) { - case RT5677: - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, - RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, - RT5677_PLL2_PR_SRC_MASK | - RT5677_DSP_CLK_SRC_MASK, - RT5677_PLL2_PR_SRC_MCLK2 | - RT5677_DSP_CLK_SRC_BYPASS); - break; - case RT5676: - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, - RT5677_DSP_CLK_SRC_MASK, - RT5677_DSP_CLK_SRC_BYPASS); - break; - default: - break; + rt5677_set_vad_source(rt5677); + rt5677_set_dsp_mode(rt5677, true); + +#define RT5677_BOOT_RETRY 20 + for (i = 0; i < RT5677_BOOT_RETRY; i++) { + regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val); + if (val == 0x3ff) + break; + udelay(500); } - regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); - regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd); - rt5677_set_dsp_mode(component, true); - - ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1, - component->dev); - if (ret == 0) { - rt5677_spi_write_firmware(0x50000000, rt5677->fw1); - release_firmware(rt5677->fw1); + if (i == RT5677_BOOT_RETRY && val != 0x3ff) { + dev_err(rt5677->component->dev, "DSP Boot Timed Out!"); + return; } - ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2, - component->dev); - if (ret == 0) { - rt5677_spi_write_firmware(0x60000000, rt5677->fw2); - release_firmware(rt5677->fw2); - } + /* Boot the firmware from IRAM instead of SRAM0. */ + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0009, 0x0003); + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0019, 0x0003); + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0009, 0x0003); - regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0); + rt5677_load_dsp_from_file(rt5677); - regcache_cache_bypass(rt5677->regmap, false); - regcache_cache_only(rt5677->regmap, true); - } else if (!on && activity) { + /* Set DSP CPU to Run */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, 0x0); + } else if (!enable && activity) { activity = false; - regcache_cache_only(rt5677->regmap, false); - regcache_cache_bypass(rt5677->regmap, true); + /* Don't turn off the DSP while handling irqs */ + mutex_lock(&rt5677->irq_lock); + /* Set DSP CPU to Stop */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); - regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1); - rt5677_set_dsp_mode(component, false); - regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001); + rt5677_set_dsp_mode(rt5677, false); - regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); + /* Disable and clear VAD interrupt */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - regcache_cache_bypass(rt5677->regmap, false); - regcache_mark_dirty(rt5677->regmap); - regcache_sync(rt5677->regmap); - } + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); - return 0; + mutex_unlock(&rt5677->irq_lock); + } } static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); @@ -805,7 +983,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = rt5677->dsp_vad_en; + ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request; return 0; } @@ -814,12 +992,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); - - rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) - rt5677_set_dsp_vad(component, rt5677->dsp_vad_en); + rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]); return 0; } @@ -3010,6 +3184,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0), /* Sidetone Mux */ SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, @@ -3544,11 +3719,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { { "SLBTX", NULL, "SLB ADC3 Mux" }, { "SLBTX", NULL, "SLB ADC4 Mux" }, + { "DSPTX", NULL, "IB01 Bypass Mux" }, + { "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" }, { "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" }, { "IB01 Mux", "SLB DAC 01", "SLB DAC01" }, { "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, - { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" }, + /* The IB01 Mux controls the source for InBound0 and InBound1. + * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to + * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for + * hotwording. "DAC1 FS" is not used currently. + * + * Creating a common widget node for "VAD ADC" + "DAC1 FS" and + * connecting the common widget to IB01 Mux causes the issue where + * there is an active path going from system playback -> "DAC1 FS" -> + * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses + * DAPM. Therefore "DAC1 FS" is ignored for now. + */ + { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" }, { "IB01 Bypass Mux", "Bypass", "IB01 Mux" }, { "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" }, @@ -4457,14 +4645,15 @@ static int rt5677_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level prev_bias = + snd_soc_component_get_bias_level(component); switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { - rt5677_set_dsp_vad(component, false); + if (prev_bias == SND_SOC_BIAS_STANDBY) { regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK, @@ -4488,9 +4677,25 @@ static int rt5677_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_STANDBY: + if (prev_bias == SND_SOC_BIAS_OFF && + rt5677->dsp_vad_en_request) { + /* Re-enable the DSP if it was turned off at suspend */ + rt5677->dsp_vad_en = true; + /* The delay is to wait for MCLK */ + schedule_delayed_work(&rt5677->dsp_work, + msecs_to_jiffies(1000)); + } break; case SND_SOC_BIAS_OFF: + flush_delayed_work(&rt5677->dsp_work); + if (rt5677->is_dsp_mode) { + /* Turn off the DSP before suspend */ + rt5677->dsp_vad_en = false; + schedule_delayed_work(&rt5677->dsp_work, 0); + flush_delayed_work(&rt5677->dsp_work); + } + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0); regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000); regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, @@ -4740,6 +4945,8 @@ static void rt5677_remove(struct snd_soc_component *component) { struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + cancel_delayed_work_sync(&rt5677->dsp_work); + regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); gpiod_set_value_cansleep(rt5677->reset_pin, 1); @@ -4750,6 +4957,11 @@ static int rt5677_suspend(struct snd_soc_component *component) { struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + if (rt5677->irq) { + cancel_delayed_work_sync(&rt5677->resume_irq_check); + disable_irq(rt5677->irq); + } + if (!rt5677->dsp_vad_en) { regcache_cache_only(rt5677->regmap, true); regcache_mark_dirty(rt5677->regmap); @@ -4778,6 +4990,11 @@ static int rt5677_resume(struct snd_soc_component *component) regcache_sync(rt5677->regmap); } + if (rt5677->irq) { + enable_irq(rt5677->irq); + schedule_delayed_work(&rt5677->resume_irq_check, 0); + } + return 0; } #else @@ -4842,6 +5059,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = { .set_tdm_slot = rt5677_set_tdm_slot, }; +static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = { + .set_sysclk = rt5677_set_dai_sysclk, + .set_pll = rt5677_set_dai_pll, +}; + static struct snd_soc_dai_driver rt5677_dai[] = { { .name = "rt5677-aif1", @@ -4938,6 +5160,18 @@ static struct snd_soc_dai_driver rt5677_dai[] = { }, .ops = &rt5677_aif_dai_ops, }, + { + .name = "rt5677-dspbuffer", + .id = RT5677_DSPBUFF, + .capture = { + .stream_name = "DSP Buffer", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &rt5677_dsp_dai_ops, + }, }; static const struct snd_soc_component_driver soc_component_dev_rt5677 = { @@ -5073,6 +5307,28 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = { }, }; +static bool rt5677_check_hotword(struct rt5677_priv *rt5677) +{ + int reg_gpio; + + if (!rt5677->is_dsp_mode) + return false; + + if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, ®_gpio)) + return false; + + /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */ + if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ) + return false; + + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + + rt5677_spi_hotword_detected(); + return true; +} + static irqreturn_t rt5677_irq(int unused, void *data) { struct rt5677_priv *rt5677 = data; @@ -5118,7 +5374,13 @@ static irqreturn_t rt5677_irq(int unused, void *data) reg_irq ^= rt5677_irq_descs[i].polarity_mask; } } - if (!irq_fired) + + /* Exit the loop only when we know for sure that GPIO1 pin + * was low at some point since irq_lock was acquired. Any event + * after that point creates a rising edge that triggers another + * call to rt5677_irq(). + */ + if (!irq_fired && !rt5677_check_hotword(rt5677)) goto exit; ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq); @@ -5129,6 +5391,7 @@ static irqreturn_t rt5677_irq(int unused, void *data) } } exit: + WARN_ON_ONCE(loop == 20); mutex_unlock(&rt5677->irq_lock); if (irq_fired) return IRQ_HANDLED; @@ -5136,6 +5399,39 @@ exit: return IRQ_NONE; } +static void rt5677_resume_irq_check(struct work_struct *work) +{ + int i, virq; + struct rt5677_priv *rt5677 = + container_of(work, struct rt5677_priv, resume_irq_check.work); + + /* This is needed to check and clear the interrupt status register + * at resume. If the headset is plugged/unplugged when the device is + * fully suspended, there won't be a rising edge at resume to trigger + * the interrupt. Without this, we miss the next unplug/plug event. + */ + rt5677_irq(0, rt5677); + + /* Call all enabled jack detect irq handlers again. This is needed in + * addition to the above check for a corner case caused by jack gpio + * debounce. After codec irq is disabled at suspend, the delayed work + * scheduled by soc-jack may run and read wrong jack gpio values, since + * the regmap is in cache only mode. At resume, there is no irq because + * rt5677_irq has already ran and cleared the irq status at suspend. + * Without this explicit check, unplug the headset right after suspend + * starts, then after resume the headset is still shown as plugged in. + */ + mutex_lock(&rt5677->irq_lock); + for (i = 0; i < RT5677_IRQ_NUM; i++) { + if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { + virq = irq_find_mapping(rt5677->domain, i); + if (virq) + handle_nested_irq(virq); + } + } + mutex_unlock(&rt5677->irq_lock); +} + static void rt5677_irq_bus_lock(struct irq_data *data) { struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); @@ -5211,6 +5507,7 @@ static int rt5677_init_irq(struct i2c_client *i2c) } mutex_init(&rt5677->irq_lock); + INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check); /* * Select RC as the debounce clock so that GPIO works even when @@ -5256,6 +5553,8 @@ static int rt5677_init_irq(struct i2c_client *i2c) if (ret) dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); + rt5677->irq = i2c->irq; + return ret; } @@ -5271,6 +5570,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) return -ENOMEM; rt5677->dev = &i2c->dev; + rt5677->set_dsp_vad = rt5677_set_dsp_vad; + INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work); i2c_set_clientdata(i2c, rt5677); if (i2c->dev.of_node) { diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 213f4b8ca269..944ae02aafc2 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1336,6 +1336,8 @@ #define RT5677_PLL_M_SFT 12 #define RT5677_PLL_M_BP (0x1 << 11) #define RT5677_PLL_M_BP_SFT 11 +#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1) +#define RT5677_PLL_UPDATE_PLL1_SFT 1 /* Global Clock Control 1 (0x80) */ #define RT5677_SCLK_SRC_MASK (0x3 << 14) @@ -1730,6 +1732,7 @@ enum { RT5677_AIF4, RT5677_AIF5, RT5677_AIFS, + RT5677_DSPBUFF, }; enum { @@ -1845,14 +1848,20 @@ struct rt5677_priv { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif - bool dsp_vad_en; + bool dsp_vad_en_request; /* DSP VAD enable/disable request */ + bool dsp_vad_en; /* dsp_work parameter */ bool is_dsp_mode; bool is_vref_slow; + struct delayed_work dsp_work; /* Interrupt handling */ struct irq_domain *domain; struct mutex irq_lock; unsigned int irq_en; + struct delayed_work resume_irq_check; + int irq; + + int (*set_dsp_vad)(struct snd_soc_component *component, bool on); }; int rt5677_sel_asrc_clk_src(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 5370e4b00104..b1713fffa3eb 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -44,6 +44,7 @@ static const struct rt5682_platform_data i2s_default_platform_data = { .dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2, .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3, .jd_src = RT5682_JD1, + .btndet_delay = 16, }; struct rt5682_priv { @@ -1027,6 +1028,18 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component, regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, RT5682_JD1_EN | RT5682_JD1_POL_NOR); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(250)); break; @@ -2445,6 +2458,8 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) &rt5682->pdata.dmic1_clk_pin); device_property_read_u32(dev, "realtek,jd-src", &rt5682->pdata.jd_src); + device_property_read_u32(dev, "realtek,btndet-delay", + &rt5682->pdata.btndet_delay); rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, "realtek,ldo1-en-gpios", 0); diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c new file mode 100644 index 000000000000..729acd874c48 --- /dev/null +++ b/sound/soc/codecs/tas2562.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the Texas Instruments TAS2562 CODEC +// Copyright (C) 2019 Texas Instruments Inc. + + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "tas2562.h" + +#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FORMAT_S32_LE) + +struct tas2562_data { + struct snd_soc_component *component; + struct gpio_desc *sdz_gpio; + struct regmap *regmap; + struct device *dev; + struct i2c_client *client; + int v_sense_slot; + int i_sense_slot; +}; + +static int tas2562_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct tas2562_data *tas2562 = + snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, + TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, TAS2562_ACTIVE); + break; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, + TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, TAS2562_MUTE); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, + TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, TAS2562_SHUTDOWN); + break; + + default: + dev_err(tas2562->dev, + "wrong power level setting %d\n", level); + return -EINVAL; + } + + return 0; +} + +static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate) +{ + int samp_rate; + int ramp_rate; + + switch (samplerate) { + case 7350: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; + break; + case 8000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; + break; + case 14700: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; + break; + case 16000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; + break; + case 22050: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; + break; + case 24000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; + break; + case 29400: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; + break; + case 32000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; + break; + case 44100: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; + break; + case 48000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; + break; + case 88200: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; + break; + case 96000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; + break; + case 176400: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; + break; + case 192000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; + break; + default: + dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n", + __func__, samplerate); + return -EINVAL; + } + + snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0, + TAS2562_TDM_CFG0_RAMPRATE_MASK, ramp_rate); + snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0, + TAS2562_TDM_CFG0_SAMPRATE_MASK, samp_rate); + + return 0; +} + +static int tas2562_set_dai_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 tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (slot_width) { + case 16: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_16B); + break; + case 24: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_24B); + break; + case 32: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_32B); + break; + + case 0: + /* Do not change slot width */ + break; + default: + dev_err(tas2562->dev, "slot width not supported"); + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth) +{ + int ret; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_16B); + tas2562->v_sense_slot = tas2562->i_sense_slot + 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_24B); + tas2562->v_sense_slot = tas2562->i_sense_slot + 4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_32B); + tas2562->v_sense_slot = tas2562->i_sense_slot + 4; + break; + + default: + dev_info(tas2562->dev, "Not supported params format\n"); + } + + ret = snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG5, + TAS2562_TDM_CFG5_VSNS_EN | TAS2562_TDM_CFG5_VSNS_SLOT_MASK, + TAS2562_TDM_CFG5_VSNS_EN | tas2562->v_sense_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG6, + TAS2562_TDM_CFG6_ISNS_EN | TAS2562_TDM_CFG6_ISNS_SLOT_MASK, + TAS2562_TDM_CFG6_ISNS_EN | tas2562->i_sense_slot); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_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 tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2562_set_bitwidth(tas2562, params_format(params)); + if (ret) { + dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret); + return ret; + } + + ret = tas2562_set_samplerate(tas2562, params_rate(params)); + if (ret) + dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret); + + return ret; +} + +static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING; + break; + default: + dev_err(tas2562->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, + TAS2562_TDM_CFG1_RX_EDGE_MASK, + asi_cfg_1); + if (ret < 0) { + dev_err(tas2562->dev, "Failed to set RX edge\n"); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case (SND_SOC_DAIFMT_I2S): + case (SND_SOC_DAIFMT_DSP_A): + case (SND_SOC_DAIFMT_DSP_B): + tdm_rx_start_slot = BIT(1); + break; + case (SND_SOC_DAIFMT_LEFT_J): + tdm_rx_start_slot = 0; + break; + default: + dev_err(tas2562->dev, "DAI Format is not found, fmt=0x%x\n", + fmt); + ret = -EINVAL; + break; + } + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, + TAS2562_TDM_CFG1_RX_OFFSET_MASK, + tdm_rx_start_slot); + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_update_bits(component, TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, + mute ? TAS2562_MUTE : 0); +} + +static int tas2562_codec_probe(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + + tas2562->component = component; + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 1); + + ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, TAS2562_MUTE); + if (ret < 0) + return ret; + + return 0; +} + +#ifdef CONFIG_PM +static int tas2562_suspend(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(tas2562->regmap, true); + regcache_mark_dirty(tas2562->regmap); + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 0); + + return 0; +} + +static int tas2562_resume(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 1); + + regcache_cache_only(tas2562->regmap, false); + + return regcache_sync(tas2562->regmap); +} +#else +#define tas2562_suspend NULL +#define tas2562_resume NULL +#endif + +static const char * const tas2562_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4, + tas2562_ASI1_src); + +static const struct snd_kcontrol_new tas2562_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2562_ASI1_src_enum); + +static int tas2562_dac_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 tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_info(tas2562->dev, "SND_SOC_DAPM_POST_PMU\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_info(tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n"); + break; + default: + break; + } + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0); + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN, + 1, 1); + +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN, + 1, 1); + +static const struct snd_kcontrol_new tas2562_snd_controls[] = { + SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0, + tas2562_dac_tlv), +}; + +static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2562_PWR_CTRL, 3, 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2562_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route tas2562_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas2562 = { + .probe = tas2562_codec_probe, + .suspend = tas2562_suspend, + .resume = tas2562_resume, + .set_bias_level = tas2562_set_bias_level, + .controls = tas2562_snd_controls, + .num_controls = ARRAY_SIZE(tas2562_snd_controls), + .dapm_widgets = tas2562_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets), + .dapm_routes = tas2562_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = { + .hw_params = tas2562_hw_params, + .set_fmt = tas2562_set_dai_fmt, + .set_tdm_slot = tas2562_set_dai_tdm_slot, + .digital_mute = tas2562_mute, +}; + +static struct snd_soc_dai_driver tas2562_dai[] = { + { + .name = "tas2562-amplifier", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, + .ops = &tas2562_speaker_dai_ops, + }, +}; + +static const struct regmap_range_cfg tas2562_ranges[] = { + { + .range_min = 0, + .range_max = 5 * 128, + .selector_reg = TAS2562_PAGE_CTRL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct reg_default tas2562_reg_defaults[] = { + { TAS2562_PAGE_CTRL, 0x00 }, + { TAS2562_SW_RESET, 0x00 }, + { TAS2562_PWR_CTRL, 0x0e }, + { TAS2562_PB_CFG1, 0x20 }, + { TAS2562_TDM_CFG0, 0x09 }, + { TAS2562_TDM_CFG1, 0x02 }, +}; + +static const struct regmap_config tas2562_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 5 * 128, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = tas2562_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults), + .ranges = tas2562_ranges, + .num_ranges = ARRAY_SIZE(tas2562_ranges), +}; + +static int tas2562_parse_dt(struct tas2562_data *tas2562) +{ + struct device *dev = tas2562->dev; + int ret = 0; + + tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down-gpio", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2562->sdz_gpio)) { + if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) { + tas2562->sdz_gpio = NULL; + return -EPROBE_DEFER; + } + } + + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2562->i_sense_slot); + if (ret) + dev_err(dev, "Looking up %s property failed %d\n", + "ti,imon-slot-no", ret); + + return ret; +} + +static int tas2562_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tas2562_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->dev = &client->dev; + + tas2562_parse_dt(data); + + data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(dev, "failed to allocate register map: %d\n", ret); + return ret; + } + + dev_set_drvdata(&client->dev, data); + + return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562, + tas2562_dai, + ARRAY_SIZE(tas2562_dai)); + +} + +static const struct i2c_device_id tas2562_id[] = { + { "tas2562", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2562_id); + +static const struct of_device_id tas2562_of_match[] = { + { .compatible = "ti,tas2562", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas2562_of_match); + +static struct i2c_driver tas2562_i2c_driver = { + .driver = { + .name = "tas2562", + .of_match_table = of_match_ptr(tas2562_of_match), + }, + .probe = tas2562_probe, + .id_table = tas2562_id, +}; + +module_i2c_driver(tas2562_i2c_driver); + +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); +MODULE_DESCRIPTION("TAS2562 Audio amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h new file mode 100644 index 000000000000..62e659ab786d --- /dev/null +++ b/sound/soc/codecs/tas2562.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas2562.h - ALSA SoC Texas Instruments TAS2562 Mono Audio Amplifier + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Dan Murphy <dmurphy@ti.com> + */ + +#ifndef __TAS2562_H__ +#define __TAS2562_H__ + +#define TAS2562_PAGE_CTRL 0x00 + +#define TAS2562_REG(page, reg) ((page * 128) + reg) + +#define TAS2562_SW_RESET TAS2562_REG(0, 0x01) +#define TAS2562_PWR_CTRL TAS2562_REG(0, 0x02) +#define TAS2562_PB_CFG1 TAS2562_REG(0, 0x03) +#define TAS2562_MISC_CFG1 TAS2562_REG(0, 0x04) +#define TAS2562_MISC_CFG2 TAS2562_REG(0, 0x05) + +#define TAS2562_TDM_CFG0 TAS2562_REG(0, 0x06) +#define TAS2562_TDM_CFG1 TAS2562_REG(0, 0x07) +#define TAS2562_TDM_CFG2 TAS2562_REG(0, 0x08) +#define TAS2562_TDM_CFG3 TAS2562_REG(0, 0x09) +#define TAS2562_TDM_CFG4 TAS2562_REG(0, 0x0a) +#define TAS2562_TDM_CFG5 TAS2562_REG(0, 0x0b) +#define TAS2562_TDM_CFG6 TAS2562_REG(0, 0x0c) +#define TAS2562_TDM_CFG7 TAS2562_REG(0, 0x0d) +#define TAS2562_TDM_CFG8 TAS2562_REG(0, 0x0e) +#define TAS2562_TDM_CFG9 TAS2562_REG(0, 0x0f) +#define TAS2562_TDM_CFG10 TAS2562_REG(0, 0x10) +#define TAS2562_TDM_DET TAS2562_REG(0, 0x11) +#define TAS2562_REV_ID TAS2562_REG(0, 0x7d) + +/* Page 2 */ +#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x01) +#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x02) + +#define TAS2562_RESET BIT(0) + +#define TAS2562_MODE_MASK 0x3 +#define TAS2562_ACTIVE 0x0 +#define TAS2562_MUTE 0x1 +#define TAS2562_SHUTDOWN 0x2 + +#define TAS2562_TDM_CFG1_RX_EDGE_MASK BIT(0) +#define TAS2562_TDM_CFG1_RX_FALLING 1 +#define TAS2562_TDM_CFG1_RX_OFFSET_MASK GENMASK(4, 0) + +#define TAS2562_TDM_CFG0_RAMPRATE_MASK BIT(5) +#define TAS2562_TDM_CFG0_RAMPRATE_44_1 BIT(5) +#define TAS2562_TDM_CFG0_SAMPRATE_MASK GENMASK(3, 1) +#define TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ 0x0 +#define TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ 0x1 +#define TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ 0x2 +#define TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ 0x3 +#define TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ 0x4 +#define TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ 0x5 +#define TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ 0x6 + +#define TAS2562_TDM_CFG2_RIGHT_JUSTIFY BIT(6) + +#define TAS2562_TDM_CFG2_RXLEN_MASK GENMASK(1, 0) +#define TAS2562_TDM_CFG2_RXLEN_16B 0x0 +#define TAS2562_TDM_CFG2_RXLEN_24B BIT(0) +#define TAS2562_TDM_CFG2_RXLEN_32B BIT(1) + +#define TAS2562_TDM_CFG2_RXWLEN_MASK GENMASK(3, 2) +#define TAS2562_TDM_CFG2_RXWLEN_16B 0x0 +#define TAS2562_TDM_CFG2_RXWLEN_20B BIT(2) +#define TAS2562_TDM_CFG2_RXWLEN_24B BIT(3) +#define TAS2562_TDM_CFG2_RXWLEN_32B (BIT(2) | BIT(3)) + +#define TAS2562_VSENSE_POWER_EN BIT(2) +#define TAS2562_ISENSE_POWER_EN BIT(3) + +#define TAS2562_TDM_CFG5_VSNS_EN BIT(6) +#define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0) + +#define TAS2562_TDM_CFG6_ISNS_EN BIT(6) +#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0) + +#endif /* __TAS2562_H__ */ diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c new file mode 100644 index 000000000000..54c8135fe43c --- /dev/null +++ b/sound/soc/codecs/tas2770.c @@ -0,0 +1,819 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D +// Audio Amplifier with Speaker I/V Sense +// +// Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Tracy Yi <tracy-yi@ti.com> +// Frank Shi <shifu0704@thundersoft.com> + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/firmware.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "tas2770.h" + +#define TAS2770_MDELAY 0xFFFFFFFE + +static void tas2770_reset(struct tas2770_priv *tas2770) +{ + if (tas2770->reset_gpio) { + gpiod_set_value_cansleep(tas2770->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(tas2770->reset_gpio, 1); + } + snd_soc_component_write(tas2770->component, TAS2770_SW_RST, + TAS2770_RST); +} + +static int tas2770_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + break; + + default: + dev_err(tas2770->dev, + "wrong power level setting %d\n", level); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_PM +static int tas2770_codec_suspend(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_codec_resume(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); + + if (ret < 0) + return ret; + + return 0; +} +#else +#define tas2770_codec_suspend NULL +#define tas2770_codec_resume NULL +#endif + +static const char * const tas2770_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2, + 4, tas2770_ASI1_src); + +static const struct snd_kcontrol_new tas2770_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum); + +static int tas2770_dac_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 tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_MUTE); + if (ret) + goto end; + break; + case SND_SOC_DAPM_PRE_PMD: + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + if (ret) + goto end; + break; + default: + dev_err(tas2770->dev, "Not supported evevt\n"); + return -EINVAL; + } + +end: + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1); +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1); + +static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, + &tas2770_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, + &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, + &vsense_switch), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tas2770_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + {"DAC", NULL, "ASI1 Sel"}, + {"OUT", NULL, "DAC"}, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static int tas2770_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + int ret; + + if (mute) + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_MUTE); + else + ret = snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) +{ + int ret; + struct snd_soc_component *component = tas2770->component; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_16BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_24BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_32BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 4; + break; + + default: + return -EINVAL; + } + + tas2770->channel_size = bitwidth; + + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG5, + TAS2770_TDM_CFG_REG5_VSNS_MASK | + TAS2770_TDM_CFG_REG5_50_MASK, + TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + tas2770->v_sense_slot); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG6, + TAS2770_TDM_CFG_REG6_ISNS_MASK | + TAS2770_TDM_CFG_REG6_50_MASK, + TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + tas2770->i_sense_slot); + +end: + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) +{ + int ret; + struct snd_soc_component *component = tas2770->component; + + switch (samplerate) { + case 48000: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + if (ret) + goto end; + break; + case 44100: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + if (ret) + goto end; + break; + case 96000: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + break; + case 88200: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + break; + case 19200: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + if (ret) + goto end; + break; + case 17640: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + if (ret) + goto end; + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + break; + default: + ret = -EINVAL; + } + +end: + if (ret < 0) + return ret; + + tas2770->sampling_rate = samplerate; + return 0; +} + +static int tas2770_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 tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2770_set_bitwidth(tas2770, params_format(params)); + if (ret < 0) + goto end; + + + ret = tas2770_set_samplerate(tas2770, params_rate(params)); + +end: + return ret; +} + +static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int ret; + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + dev_err(tas2770->dev, "ASI format master is not found\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING; + break; + default: + dev_err(tas2770->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, + TAS2770_TDM_CFG_REG1_RX_MASK, + asi_cfg_1); + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + tdm_rx_start_slot = 1; + break; + case SND_SOC_DAIFMT_DSP_A: + tdm_rx_start_slot = 0; + break; + case SND_SOC_DAIFMT_DSP_B: + tdm_rx_start_slot = 1; + break; + case SND_SOC_DAIFMT_LEFT_J: + tdm_rx_start_slot = 0; + break; + default: + dev_err(tas2770->dev, + "DAI Format is not found, fmt=0x%x\n", fmt); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, + TAS2770_TDM_CFG_REG1_MASK, + (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT)); + if (ret < 0) + return ret; + + tas2770->asi_format = fmt; + + return 0; +} + +static int tas2770_set_dai_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 tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + int left_slot, right_slot; + int ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + left_slot = 0; + right_slot = 0; + } else { + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, + TAS2770_TDM_CFG_REG3_30_MASK, + (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT)); + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, + TAS2770_TDM_CFG_REG3_RXS_MASK, + (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT)); + if (ret < 0) + return ret; + + switch (slot_width) { + case 16: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_16BITS); + break; + + case 24: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_24BITS); + break; + + case 32: + ret = snd_soc_component_update_bits(component, + TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_32BITS); + break; + + case 0: + /* Do not change slot width */ + ret = 0; + break; + + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + tas2770->slot_width = slot_width; + return 0; +} + +static struct snd_soc_dai_ops tas2770_dai_ops = { + .digital_mute = tas2770_mute, + .hw_params = tas2770_hw_params, + .set_fmt = tas2770_set_fmt, + .set_tdm_slot = tas2770_set_dai_tdm_slot, +}; + +#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000\ + ) + +static struct snd_soc_dai_driver tas2770_dai_driver[] = { + { + .name = "tas2770 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TAS2770_RATES, + .formats = TAS2770_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = TAS2770_RATES, + .formats = TAS2770_FORMATS, + }, + .ops = &tas2770_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int tas2770_codec_probe(struct snd_soc_component *component) +{ + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + + tas2770->component = component; + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); + +static const struct snd_kcontrol_new tas2770_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2, + 0, TAS2770_PLAY_CFG_REG2_VMAX, 1, + tas2770_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0, + 0, 0x14, 0, + tas2770_digital_tlv), +}; + +static const struct snd_soc_component_driver soc_component_driver_tas2770 = { + .probe = tas2770_codec_probe, + .suspend = tas2770_codec_suspend, + .resume = tas2770_codec_resume, + .set_bias_level = tas2770_set_bias_level, + .controls = tas2770_snd_controls, + .num_controls = ARRAY_SIZE(tas2770_snd_controls), + .dapm_widgets = tas2770_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets), + .dapm_routes = tas2770_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2770_audio_map), + .idle_bias_on = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static int tas2770_register_codec(struct tas2770_priv *tas2770) +{ + return devm_snd_soc_register_component(tas2770->dev, + &soc_component_driver_tas2770, + tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver)); +} + +static const struct reg_default tas2770_reg_defaults[] = { + { TAS2770_PAGE, 0x00 }, + { TAS2770_SW_RST, 0x00 }, + { TAS2770_PWR_CTRL, 0x0e }, + { TAS2770_PLAY_CFG_REG0, 0x10 }, + { TAS2770_PLAY_CFG_REG1, 0x01 }, + { TAS2770_PLAY_CFG_REG2, 0x00 }, + { TAS2770_MSC_CFG_REG0, 0x07 }, + { TAS2770_TDM_CFG_REG1, 0x02 }, + { TAS2770_TDM_CFG_REG2, 0x0a }, + { TAS2770_TDM_CFG_REG3, 0x10 }, + { TAS2770_INT_MASK_REG0, 0xfc }, + { TAS2770_INT_MASK_REG1, 0xb1 }, + { TAS2770_INT_CFG, 0x05 }, + { TAS2770_MISC_IRQ, 0x81 }, + { TAS2770_CLK_CGF, 0x0c }, + +}; + +static bool tas2770_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS2770_PAGE: /* regmap implementation requires this */ + case TAS2770_SW_RST: /* always clears after write */ + case TAS2770_BO_PRV_REG0:/* has a self clearing bit */ + case TAS2770_LVE_INT_REG0: + case TAS2770_LVE_INT_REG1: + case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */ + case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */ + case TAS2770_VBAT_MSB: + case TAS2770_VBAT_LSB: + case TAS2770_TEMP_MSB: + case TAS2770_TEMP_LSB: + return true; + } + return false; +} + +static bool tas2770_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS2770_LVE_INT_REG0: + case TAS2770_LVE_INT_REG1: + case TAS2770_LAT_INT_REG0: + case TAS2770_LAT_INT_REG1: + case TAS2770_VBAT_MSB: + case TAS2770_VBAT_LSB: + case TAS2770_TEMP_MSB: + case TAS2770_TEMP_LSB: + case TAS2770_TDM_CLK_DETC: + case TAS2770_REV_AND_GPID: + return false; + } + return true; +} + +static const struct regmap_range_cfg tas2770_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 1 * 128, + .selector_reg = TAS2770_PAGE, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tas2770_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2770_writeable, + .volatile_reg = tas2770_volatile, + .reg_defaults = tas2770_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = tas2770_regmap_ranges, + .num_ranges = ARRAY_SIZE(tas2770_regmap_ranges), + .max_register = 1 * 128, +}; + +static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) +{ + int rc = 0; + + rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format", + &tas2770->asi_format); + if (rc) { + dev_err(tas2770->dev, "Looking up %s property failed %d\n", + "ti,asi-format", rc); + goto end; + } + + rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2770->i_sense_slot); + if (rc) { + dev_err(tas2770->dev, "Looking up %s property failed %d\n", + "ti,imon-slot-no", rc); + goto end; + } + + rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2770->v_sense_slot); + if (rc) { + dev_err(tas2770->dev, "Looking up %s property failed %d\n", + "ti,vmon-slot-no", rc); + goto end; + } + +end: + return rc; +} + +static int tas2770_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas2770_priv *tas2770; + int result; + + tas2770 = devm_kzalloc(&client->dev, + sizeof(struct tas2770_priv), GFP_KERNEL); + if (!tas2770) + return -ENOMEM; + tas2770->dev = &client->dev; + + i2c_set_clientdata(client, tas2770); + dev_set_drvdata(&client->dev, tas2770); + tas2770->power_state = TAS2770_POWER_SHUTDOWN; + + tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap); + if (IS_ERR(tas2770->regmap)) { + result = PTR_ERR(tas2770->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + result); + goto end; + } + + if (client->dev.of_node) { + result = tas2770_parse_dt(&client->dev, tas2770); + if (result) { + dev_err(tas2770->dev, "%s: Failed to parse devicetree\n", + __func__); + goto end; + } + } + + tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev, + "reset-gpio", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2770->reset_gpio)) { + if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) { + tas2770->reset_gpio = NULL; + return -EPROBE_DEFER; + } + } + + tas2770->channel_size = 0; + tas2770->slot_width = 0; + + tas2770_reset(tas2770); + + result = tas2770_register_codec(tas2770); + if (result) + dev_err(tas2770->dev, "Register codec failed.\n"); + +end: + return result; +} + +static int tas2770_i2c_remove(struct i2c_client *client) +{ + pm_runtime_disable(&client->dev); + return 0; +} + + +static const struct i2c_device_id tas2770_i2c_id[] = { + { "tas2770", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2770_of_match[] = { + { .compatible = "ti,tas2770" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2770_of_match); +#endif + +static struct i2c_driver tas2770_i2c_driver = { + .driver = { + .name = "tas2770", + .of_match_table = of_match_ptr(tas2770_of_match), + }, + .probe = tas2770_i2c_probe, + .remove = tas2770_i2c_remove, + .id_table = tas2770_i2c_id, +}; + +module_i2c_driver(tas2770_i2c_driver); + +MODULE_AUTHOR("Shi Fu <shifu0704@thundersoft.com>"); +MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h new file mode 100644 index 000000000000..cbb858369fe6 --- /dev/null +++ b/sound/soc/codecs/tas2770.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC TAS2770 codec driver + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ + */ +#ifndef __TAS2770__ +#define __TAS2770__ + +/* Book Control Register (available in page0 of each book) */ +#define TAS2770_BOOKCTL_PAGE 0 +#define TAS2770_BOOKCTL_REG 127 +#define TAS2770_REG(page, reg) ((page * 128) + reg) + /* Page */ +#define TAS2770_PAGE TAS2770_REG(0X0, 0x00) +#define TAS2770_PAGE_PAGE_MASK 255 + /* Software Reset */ +#define TAS2770_SW_RST TAS2770_REG(0X0, 0x01) +#define TAS2770_RST BIT(0) + /* Power Control */ +#define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02) +#define TAS2770_PWR_CTRL_MASK 0x3 +#define TAS2770_PWR_CTRL_ACTIVE 0x0 +#define TAS2770_PWR_CTRL_MUTE BIT(0) +#define TAS2770_PWR_CTRL_SHUTDOWN 0x2 + /* Playback Configuration Reg0 */ +#define TAS2770_PLAY_CFG_REG0 TAS2770_REG(0X0, 0x03) + /* Playback Configuration Reg1 */ +#define TAS2770_PLAY_CFG_REG1 TAS2770_REG(0X0, 0x04) + /* Playback Configuration Reg2 */ +#define TAS2770_PLAY_CFG_REG2 TAS2770_REG(0X0, 0x05) +#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9 + /* Misc Configuration Reg0 */ +#define TAS2770_MSC_CFG_REG0 TAS2770_REG(0X0, 0x07) + /* TDM Configuration Reg0 */ +#define TAS2770_TDM_CFG_REG0 TAS2770_REG(0X0, 0x0A) +#define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5) +#define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0 +#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5) +#define TAS2770_TDM_CFG_REG0_31_MASK 0xe +#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6 +#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8 +#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa + /* TDM Configuration Reg1 */ +#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B) +#define TAS2770_TDM_CFG_REG1_MASK 0x3e +#define TAS2770_TDM_CFG_REG1_51_SHIFT 1 +#define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0) +#define TAS2770_TDM_CFG_REG1_RX_RSING 0x0 +#define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0) + /* TDM Configuration Reg2 */ +#define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C) +#define TAS2770_TDM_CFG_REG2_RXW_MASK 0xc +#define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0 +#define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8 +#define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc +#define TAS2770_TDM_CFG_REG2_RXS_MASK 0x3 +#define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0 +#define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0) +#define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2 + /* TDM Configuration Reg3 */ +#define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D) +#define TAS2770_TDM_CFG_REG3_RXS_MASK 0xf0 +#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 +#define TAS2770_TDM_CFG_REG3_30_MASK 0xf +#define TAS2770_TDM_CFG_REG3_30_SHIFT 0 + /* TDM Configuration Reg5 */ +#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) +#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) +#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG5_50_MASK 0x3f + /* TDM Configuration Reg6 */ +#define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10) +#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6) +#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG6_50_MASK 0x3f + /* Brown Out Prevention Reg0 */ +#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B) + /* Interrupt MASK Reg0 */ +#define TAS2770_INT_MASK_REG0 TAS2770_REG(0X0, 0x20) +#define TAS2770_INT_REG0_DEFAULT 0xfc +#define TAS2770_INT_MASK_REG0_DISABLE 0xff + /* Interrupt MASK Reg1 */ +#define TAS2770_INT_MASK_REG1 TAS2770_REG(0X0, 0x21) +#define TAS2770_INT_REG1_DEFAULT 0xb1 +#define TAS2770_INT_MASK_REG1_DISABLE 0xff + /* Live-Interrupt Reg0 */ +#define TAS2770_LVE_INT_REG0 TAS2770_REG(0X0, 0x22) + /* Live-Interrupt Reg1 */ +#define TAS2770_LVE_INT_REG1 TAS2770_REG(0X0, 0x23) + /* Latched-Interrupt Reg0 */ +#define TAS2770_LAT_INT_REG0 TAS2770_REG(0X0, 0x24) +#define TAS2770_LAT_INT_REG0_OCE_FLG BIT(1) +#define TAS2770_LAT_INT_REG0_OTE_FLG BIT(0) + /* Latched-Interrupt Reg1 */ +#define TAS2770_LAT_INT_REG1 TAS2770_REG(0X0, 0x25) +#define TAS2770_LAT_INT_REG1_VBA_TOV BIT(3) +#define TAS2770_LAT_INT_REG1_VBA_TUV BIT(2) +#define TAS2770_LAT_INT_REG1_BOUT_FLG BIT(1) + /* VBAT MSB */ +#define TAS2770_VBAT_MSB TAS2770_REG(0X0, 0x27) + /* VBAT LSB */ +#define TAS2770_VBAT_LSB TAS2770_REG(0X0, 0x28) + /* TEMP MSB */ +#define TAS2770_TEMP_MSB TAS2770_REG(0X0, 0x29) + /* TEMP LSB */ +#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A) + /* Interrupt Configuration */ +#define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) + /* Misc IRQ */ +#define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32) + /* Clock Configuration */ +#define TAS2770_CLK_CGF TAS2770_REG(0X0, 0x3C) + /* TDM Clock detection monitor */ +#define TAS2770_TDM_CLK_DETC TAS2770_REG(0X0, 0x77) + /* Revision and PG ID */ +#define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D) + +#define TAS2770_POWER_ACTIVE 0 +#define TAS2770_POWER_MUTE 1 +#define TAS2770_POWER_SHUTDOWN 2 +#define ERROR_OVER_CURRENT 0x0000001 +#define ERROR_DIE_OVERTEMP 0x0000002 +#define ERROR_OVER_VOLTAGE 0x0000004 +#define ERROR_UNDER_VOLTAGE 0x0000008 +#define ERROR_BROWNOUT 0x0000010 +#define ERROR_CLASSD_PWR 0x0000020 + +struct tas2770_priv { + struct device *dev; + struct regmap *regmap; + struct snd_soc_component *component; + int power_state; + int asi_format; + struct gpio_desc *reset_gpio; + int sampling_rate; + int channel_size; + int slot_width; + int v_sense_slot; + int i_sense_slot; +}; + +#endif /* __TAS2770__ */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index df627a08def9..f6f19fdc72f5 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -171,6 +171,7 @@ struct aic31xx_priv { int rate_div_line; bool master_dapm_route_applied; int irq; + u8 ocmv; /* output common-mode voltage */ }; struct aic31xx_rate_divs { @@ -1312,6 +1313,11 @@ static int aic31xx_codec_probe(struct snd_soc_component *component) if (ret) return ret; + /* set output common-mode voltage */ + snd_soc_component_update_bits(component, AIC31XX_HPDRIVER, + AIC31XX_HPD_OCMV_MASK, + aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT); + return 0; } @@ -1501,6 +1507,43 @@ exit: return IRQ_NONE; } +static void aic31xx_configure_ocmv(struct aic31xx_priv *priv) +{ + struct device *dev = priv->dev; + int dvdd, avdd; + u32 value; + + if (dev->fwnode && + fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) { + /* OCMV setting is forced by DT */ + if (value <= 3) { + priv->ocmv = value; + return; + } + } + + avdd = regulator_get_voltage(priv->supplies[3].consumer); + dvdd = regulator_get_voltage(priv->supplies[5].consumer); + + if (avdd > 3600000 || dvdd > 1950000) { + dev_warn(dev, + "Too high supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } else if (avdd == 3600000 && dvdd == 1950000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_8V; + } else if (avdd >= 3300000 && dvdd >= 1800000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_65V; + } else if (avdd >= 3000000 && dvdd >= 1650000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_5V; + } else if (avdd >= 2700000 && dvdd >= 1525000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_35V; + } else { + dev_warn(dev, + "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } +} + static int aic31xx_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1570,6 +1613,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, return ret; } + aic31xx_configure_ocmv(aic31xx); + if (aic31xx->irq > 0) { regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1, AIC31XX_GPIO1_FUNC_MASK, diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h index cb024955c978..83a8c7604cc3 100644 --- a/sound/soc/codecs/tlv320aic31xx.h +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -232,6 +232,14 @@ struct aic31xx_pdata { #define AIC31XX_HSD_HP 0x01 #define AIC31XX_HSD_HS 0x03 +/* AIC31XX_HPDRIVER */ +#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3) +#define AIC31XX_HPD_OCMV_SHIFT 3 +#define AIC31XX_HPD_OCMV_1_35V 0x0 +#define AIC31XX_HPD_OCMV_1_5V 0x1 +#define AIC31XX_HPD_OCMV_1_65V 0x2 +#define AIC31XX_HPD_OCMV_1_8V 0x3 + /* AIC31XX_MICBIAS */ #define AIC31XX_MICBIAS_MASK GENMASK(1, 0) #define AIC31XX_MICBIAS_SHIFT 0 diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 68165de1c8de..b4e9a6c73f90 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -573,6 +573,9 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct clk *pll; pll = devm_clk_get(component->dev, "pll"); + if (IS_ERR(pll)) + return PTR_ERR(pll); + mclk = clk_get_parent(pll); return clk_set_rate(mclk, freq); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f318403133e9..f11ffa28683b 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -2837,11 +2837,11 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, TX_HPF_CUT_OFF_FREQ_MASK) >> 5; snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10); snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00); - if (hpf_coff_freq != CF_MIN_3DB_150HZ) { - snd_soc_component_update_bits(comp, dec_cfg_reg, - TX_HPF_CUT_OFF_FREQ_MASK, - hpf_coff_freq << 5); - } + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + } break; case SND_SOC_DAPM_POST_PMD: snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index cf64e109c658..7b087d94141b 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2410,6 +2410,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, err_pm_runtime: pm_runtime_disable(&i2c->dev); + if (i2c->irq) + free_irq(i2c->irq, wm2200); err_reset: if (wm2200->pdata.reset) gpio_set_value_cansleep(wm2200->pdata.reset, 0); @@ -2426,12 +2428,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c) { struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c); + pm_runtime_disable(&i2c->dev); if (i2c->irq) free_irq(i2c->irq, wm2200); if (wm2200->pdata.reset) gpio_set_value_cansleep(wm2200->pdata.reset, 0); if (wm2200->pdata.ldo_ena) gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); return 0; } diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4af0e519e623..91cc63c5a51f 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2617,6 +2617,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c, return ret; err_reset: + pm_runtime_disable(&i2c->dev); if (i2c->irq) free_irq(i2c->irq, wm5100); wm5100_free_gpio(i2c); @@ -2640,6 +2641,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c) { struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c); + pm_runtime_disable(&i2c->dev); if (i2c->irq) free_irq(i2c->irq, wm5100); wm5100_free_gpio(i2c); diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9e8c564f6e9c..7d7ea15d73e0 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1410,34 +1410,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream, return 0; } - -static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct snd_soc_component *component = dai->component; - struct wm8904_priv *priv = snd_soc_component_get_drvdata(component); - - switch (clk_id) { - case WM8904_CLK_MCLK: - priv->sysclk_src = clk_id; - priv->mclk_rate = freq; - break; - - case WM8904_CLK_FLL: - priv->sysclk_src = clk_id; - break; - - default: - return -EINVAL; - } - - dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); - - wm8904_configure_clocking(component); - - return 0; -} - static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; @@ -1824,6 +1796,50 @@ out: return 0; } +static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8904_priv *priv = snd_soc_component_get_drvdata(component); + unsigned long mclk_freq; + int ret; + + switch (clk_id) { + case WM8904_CLK_AUTO: + mclk_freq = clk_get_rate(priv->mclk); + /* enable FLL if a different sysclk is desired */ + if (mclk_freq != freq) { + priv->sysclk_src = WM8904_CLK_FLL; + ret = wm8904_set_fll(dai, WM8904_FLL_MCLK, + WM8904_FLL_MCLK, + mclk_freq, freq); + if (ret) + return ret; + break; + } + clk_id = WM8904_CLK_MCLK; + /* fallthrough */ + + case WM8904_CLK_MCLK: + priv->sysclk_src = clk_id; + priv->mclk_rate = freq; + break; + + case WM8904_CLK_FLL: + priv->sysclk_src = clk_id; + break; + + default: + return -EINVAL; + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + wm8904_configure_clocking(component); + + return 0; +} + static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_component *component = codec_dai->component; diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h index c1bca52f9927..de6340446b1f 100644 --- a/sound/soc/codecs/wm8904.h +++ b/sound/soc/codecs/wm8904.h @@ -10,6 +10,7 @@ #ifndef _WM8904_H #define _WM8904_H +#define WM8904_CLK_AUTO 0 #define WM8904_CLK_MCLK 1 #define WM8904_CLK_FLL 2 diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 18535b326680..ca42445b649d 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -25,6 +25,8 @@ #include <linux/mfd/wm8994/pdata.h> #include <linux/mfd/wm8994/gpio.h> +#include <asm/unaligned.h> + #include "wm8994.h" #define WM_FW_BLOCK_INFO 0xff @@ -58,18 +60,15 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name, } if (memcmp(fw->data, "WMFW", 4) != 0) { - memcpy(&data32, fw->data, sizeof(data32)); - data32 = be32_to_cpu(data32); + data32 = get_unaligned_be32(fw->data); dev_err(component->dev, "%s: firmware has bad file magic %08x\n", name, data32); goto err; } - memcpy(&data32, fw->data + 4, sizeof(data32)); - len = be32_to_cpu(data32); + len = get_unaligned_be32(fw->data + 4); + data32 = get_unaligned_be32(fw->data + 8); - memcpy(&data32, fw->data + 8, sizeof(data32)); - data32 = be32_to_cpu(data32); if ((data32 >> 24) & 0xff) { dev_err(component->dev, "%s: unsupported firmware version %d\n", name, (data32 >> 24) & 0xff); @@ -87,9 +86,8 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name, } if (check) { - memcpy(&data64, fw->data + 24, sizeof(u64)); - dev_info(component->dev, "%s timestamp %llx\n", - name, be64_to_cpu(data64)); + data64 = get_unaligned_be64(fw->data + 24); + dev_info(component->dev, "%s timestamp %llx\n", name, data64); } else { snd_soc_component_write(component, 0x102, 0x2); snd_soc_component_write(component, 0x900, 0x2); @@ -104,8 +102,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name, goto err; } - memcpy(&data32, data + 4, sizeof(data32)); - block_len = be32_to_cpu(data32); + block_len = get_unaligned_be32(data + 4); if (block_len + 8 > len) { dev_err(component->dev, "%zd byte block longer than file\n", block_len); @@ -116,8 +113,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name, goto err; } - memcpy(&data32, data, sizeof(data32)); - data32 = be32_to_cpu(data32); + data32 = get_unaligned_be32(data); switch ((data32 >> 24) & 0xff) { case WM_FW_BLOCK_INFO: diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index d5fb7f5dd551..15ce64a48a87 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -167,12 +167,12 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif) switch (wm8994->sysclk[aif]) { case WM8994_SYSCLK_MCLK1: - rate = wm8994->mclk[0]; + rate = wm8994->mclk_rate[0]; break; case WM8994_SYSCLK_MCLK2: reg1 |= 0x8; - rate = wm8994->mclk[1]; + rate = wm8994->mclk_rate[1]; break; case WM8994_SYSCLK_FLL1: @@ -1038,6 +1038,45 @@ static bool wm8994_check_class_w_digital(struct snd_soc_component *component) return true; } +static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + unsigned int offset, val, clk_idx; + int ret; + + if (aif) + offset = 4; + else + offset = 0; + + val = snd_soc_component_read32(component, WM8994_AIF1_CLOCKING_1 + offset); + val &= WM8994_AIF1CLK_SRC_MASK; + + switch (val) { + case 0: + clk_idx = WM8994_MCLK1; + break; + case 1: + clk_idx = WM8994_MCLK2; + break; + default: + return 0; + } + + if (enable) { + ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK%d\n", + clk_idx); + return ret; + } + } else { + clk_disable_unprepare(wm8994->mclk[clk_idx].clk); + } + + return 0; +} + static int aif1clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1045,7 +1084,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w, struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); struct wm8994 *control = wm8994->wm8994; int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA; - int i; + int ret, i; int dac; int adc; int val; @@ -1061,6 +1100,10 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + ret = aif_mclk_set(component, 0, true); + if (ret < 0) + return ret; + /* Don't enable timeslot 2 if not in use */ if (wm8994->channels[0] <= 2) mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); @@ -1133,6 +1176,12 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w, break; } + switch (event) { + case SND_SOC_DAPM_POST_PMD: + aif_mclk_set(component, 0, false); + break; + } + return 0; } @@ -1140,13 +1189,17 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - int i; + int ret, i; int dac; int adc; int val; switch (event) { case SND_SOC_DAPM_PRE_PMU: + ret = aif_mclk_set(component, 1, true); + if (ret < 0) + return ret; + val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_1); if ((val & WM8994_AIF2ADCL_SRC) && (val & WM8994_AIF2ADCR_SRC)) @@ -1218,6 +1271,12 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w, break; } + switch (event) { + case SND_SOC_DAPM_POST_PMD: + aif_mclk_set(component, 1, false); + break; + } + return 0; } @@ -1623,10 +1682,10 @@ SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), @@ -2141,6 +2200,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, u16 reg, clk1, aif_reg, aif_src; unsigned long timeout; bool was_enabled; + struct clk *mclk; switch (id) { case WM8994_FLL1: @@ -2216,6 +2276,27 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset, WM8994_FLL1_ENA, 0); + /* Disable MCLK if needed before we possibly change to new clock parent */ + if (was_enabled) { + reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_5 + + reg_offset); + reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK) + >> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1; + + switch (reg) { + case WM8994_FLL_SRC_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; + break; + case WM8994_FLL_SRC_MCLK2: + mclk = wm8994->mclk[WM8994_MCLK2].clk; + break; + default: + mclk = NULL; + } + + clk_disable_unprepare(mclk); + } + if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK && freq_in == freq_out && freq_out) { dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1); @@ -2260,10 +2341,29 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, /* Clear any pending completion from a previous failure */ try_wait_for_completion(&wm8994->fll_locked[id]); + switch (src) { + case WM8994_FLL_SRC_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; + break; + case WM8994_FLL_SRC_MCLK2: + mclk = wm8994->mclk[WM8994_MCLK2].clk; + break; + default: + mclk = NULL; + } + /* Enable (with fractional mode if required) */ if (freq_out) { + ret = clk_prepare_enable(mclk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK for FLL%d\n", + id + 1); + return ret; + } + /* Enable VMID if we need it */ if (!was_enabled) { + active_reference(component); switch (control->type) { @@ -2372,12 +2472,29 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src, return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out); } +static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id, + unsigned int *freq) +{ + int ret; + + if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id]) + return 0; + + ret = clk_set_rate(wm8994->mclk[id].clk, *freq); + if (ret < 0) + return ret; + + *freq = clk_get_rate(wm8994->mclk[id].clk); + + return 0; +} + static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_component *component = dai->component; struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - int i; + int ret, i; switch (dai->id) { case 1: @@ -2392,7 +2509,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, switch (clk_id) { case WM8994_SYSCLK_MCLK1: wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1; - wm8994->mclk[0] = freq; + + ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + if (ret < 0) + return ret; + + wm8994->mclk_rate[0] = freq; dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n", dai->id, freq); break; @@ -2400,7 +2522,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, case WM8994_SYSCLK_MCLK2: /* TODO: Set GPIO AF */ wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2; - wm8994->mclk[1] = freq; + + ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + if (ret < 0) + return ret; + + wm8994->mclk_rate[1] = freq; dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", dai->id, freq); break; @@ -4456,6 +4583,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8994 = { static int wm8994_probe(struct platform_device *pdev) { struct wm8994_priv *wm8994; + int ret; wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv), GFP_KERNEL); @@ -4467,6 +4595,16 @@ static int wm8994_probe(struct platform_device *pdev) wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); + wm8994->mclk[WM8994_MCLK1].id = "MCLK1"; + wm8994->mclk[WM8994_MCLK2].id = "MCLK2"; + + ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk), + wm8994->mclk); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret); + return ret; + } + pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 1d6f2abe1c11..41c4b126114d 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -6,6 +6,7 @@ #ifndef _WM8994_H #define _WM8994_H +#include <linux/clk.h> #include <sound/soc.h> #include <linux/firmware.h> #include <linux/completion.h> @@ -14,6 +15,12 @@ #include "wm_hubs.h" +enum { + WM8994_MCLK1, + WM8994_MCLK2, + WM8994_NUM_MCLK +}; + /* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ #define WM8994_SYSCLK_MCLK1 1 #define WM8994_SYSCLK_MCLK2 2 @@ -73,9 +80,10 @@ struct wm8994; struct wm8994_priv { struct wm_hubs_data hubs; struct wm8994 *wm8994; + struct clk_bulk_data mclk[WM8994_NUM_MCLK]; int sysclk[2]; int sysclk_rate[2]; - int mclk[2]; + int mclk_rate[2]; int aifclk[2]; int aifdiv[2]; int channels[2]; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 9b8bb7bbe945..2a9b610f6d43 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -599,6 +599,9 @@ struct wm_coeff_ctl_ops { struct wm_coeff_ctl { const char *name; const char *fw_name; + /* Subname is needed to match with firmware */ + const char *subname; + unsigned int subname_len; struct wm_adsp_alg_region alg_region; struct wm_coeff_ctl_ops ops; struct wm_adsp *dsp; @@ -1399,6 +1402,7 @@ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) { kfree(ctl->cache); kfree(ctl->name); + kfree(ctl->subname); kfree(ctl); } @@ -1472,6 +1476,15 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ret = -ENOMEM; goto err_ctl; } + if (subname) { + ctl->subname_len = subname_len; + ctl->subname = kmemdup(subname, + strlen(subname) + 1, GFP_KERNEL); + if (!ctl->subname) { + ret = -ENOMEM; + goto err_ctl_name; + } + } ctl->enabled = 1; ctl->set = 0; ctl->ops.xget = wm_coeff_get; @@ -1485,7 +1498,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->cache = kzalloc(ctl->len, GFP_KERNEL); if (!ctl->cache) { ret = -ENOMEM; - goto err_ctl_name; + goto err_ctl_subname; } list_add(&ctl->list, &dsp->ctl_list); @@ -1508,6 +1521,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, err_ctl_cache: kfree(ctl->cache); +err_ctl_subname: + kfree(ctl->subname); err_ctl_name: kfree(ctl->name); err_ctl: @@ -1995,6 +2010,70 @@ out: return ret; } +/* + * Find wm_coeff_ctl with input name as its subname + * If not found, return NULL + */ +static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp, + const char *name, int type, + unsigned int alg) +{ + struct wm_coeff_ctl *pos, *rslt = NULL; + + list_for_each_entry(pos, &dsp->ctl_list, list) { + if (!pos->subname) + continue; + if (strncmp(pos->subname, name, pos->subname_len) == 0 && + pos->alg_region.alg == alg && + pos->alg_region.type == type) { + rslt = pos; + break; + } + } + + return rslt; +} + +int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) +{ + struct wm_coeff_ctl *ctl; + struct snd_kcontrol *kcontrol; + int ret; + + ctl = wm_adsp_get_ctl(dsp, name, type, alg); + if (!ctl) + return -EINVAL; + + if (len > ctl->len) + return -EINVAL; + + ret = wm_coeff_write_control(ctl, buf, len); + + kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name); + snd_ctl_notify(dsp->component->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); + +int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) +{ + struct wm_coeff_ctl *ctl; + + ctl = wm_adsp_get_ctl(dsp, name, type, alg); + if (!ctl) + return -EINVAL; + + if (len > ctl->len) + return -EINVAL; + + return wm_coeff_read_control(ctl, buf, len); +} +EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); + static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index aa634ef6c9f5..4c481cf20275 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -201,5 +201,9 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, struct snd_compr_tstamp *tstamp); int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, size_t count); +int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len); +int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len); #endif diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c index a9ae91c4597f..de6fcc808832 100644 --- a/sound/soc/dwc/dwc-pcm.c +++ b/sound/soc/dwc/dwc-pcm.c @@ -135,7 +135,8 @@ void dw_pcm_pop_rx(struct dw_i2s_dev *dev) dw_pcm_transfer(dev, false); } -static int dw_pcm_open(struct snd_pcm_substream *substream) +static int dw_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -148,14 +149,16 @@ static int dw_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int dw_pcm_close(struct snd_pcm_substream *substream) +static int dw_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { synchronize_rcu(); return 0; } -static int dw_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int dw_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_pcm_runtime *runtime = substream->runtime; struct dw_i2s_dev *dev = runtime->private_data; @@ -192,12 +195,14 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int dw_pcm_hw_free(struct snd_pcm_substream *substream) +static int dw_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int dw_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct dw_i2s_dev *dev = runtime->private_data; @@ -231,7 +236,8 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct dw_i2s_dev *dev = runtime->private_data; @@ -245,7 +251,8 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream) return pos < runtime->buffer_size ? pos : 0; } -static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int dw_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { size_t size = dw_pcm_hardware.buffer_bytes_max; @@ -255,25 +262,22 @@ static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void dw_pcm_free(struct snd_pcm *pcm) +static void dw_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); } -static const struct snd_pcm_ops dw_pcm_ops = { - .open = dw_pcm_open, - .close = dw_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dw_pcm_hw_params, - .hw_free = dw_pcm_hw_free, - .trigger = dw_pcm_trigger, - .pointer = dw_pcm_pointer, -}; - static const struct snd_soc_component_driver dw_pcm_component = { - .pcm_new = dw_pcm_new, - .pcm_free = dw_pcm_free, - .ops = &dw_pcm_ops, + .open = dw_pcm_open, + .close = dw_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = dw_pcm_hw_params, + .hw_free = dw_pcm_hw_free, + .trigger = dw_pcm_trigger, + .pointer = dw_pcm_pointer, + .pcm_construct = dw_pcm_new, + .pcm_destruct = dw_pcm_free, }; int dw_pcm_register(struct platform_device *pdev) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index aa99c008a925..65e8cd4be930 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -25,6 +25,16 @@ config SND_SOC_FSL_SAI This option is only useful for out-of-tree drivers since in-tree drivers select it automatically. +config SND_SOC_FSL_MQS + tristate "Medium Quality Sound (MQS) module support" + depends on SND_SOC_FSL_SAI + select REGMAP_MMIO + help + Say Y if you want to add Medium Quality Sound (MQS) + support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + config SND_SOC_FSL_AUDMIX tristate "Audio Mixer (AUDMIX) module support" select REGMAP_MMIO diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index c0dd04422fe9..8cde88c72d93 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -23,6 +23,7 @@ snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-micfil-objs := fsl_micfil.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +snd-soc-fsl-mqs-objs := fsl_mqs.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o @@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o +obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o # MPC5200 Platform Support diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index cfa40ef6b1ca..a3cfceea7d2f 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -115,7 +115,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate, * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A * while pair A and pair C are comparatively independent. */ -static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) +int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) { enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc_priv = pair->asrc_priv; @@ -158,7 +158,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) * * It clears the resource from asrc_priv and releases the occupied channels. */ -static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) +void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) { struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; @@ -259,14 +259,24 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, * It configures those ASRC registers according to a configuration instance * of struct asrc_config which includes in/output sample rate, width, channel * and clock settings. + * + * Note: + * The ideal ratio configuration can work with a flexible clock rate setting. + * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC. + * For a regular audio playback, the clock rate should not be slower than an + * clock rate aligning with the output sample rate; For a use case requiring + * faster conversion, set use_ideal_rate to have the faster speed. */ -static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) +static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) { struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; + enum asrc_word_width input_word_width; + enum asrc_word_width output_word_width; u32 inrate, outrate, indiv, outdiv; - u32 clk_index[2], div[2]; + u32 clk_index[2], div[2], rem[2]; + u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; @@ -283,9 +293,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } - /* Validate output width */ - if (config->output_word_width == ASRC_WIDTH_8_BIT) { - pair_err("does not support 8bit width output\n"); + switch (snd_pcm_format_width(config->input_format)) { + case 8: + input_word_width = ASRC_WIDTH_8_BIT; + break; + case 16: + input_word_width = ASRC_WIDTH_16_BIT; + break; + case 24: + input_word_width = ASRC_WIDTH_24_BIT; + break; + default: + pair_err("does not support this input format, %d\n", + config->input_format); + return -EINVAL; + } + + switch (snd_pcm_format_width(config->output_format)) { + case 16: + output_word_width = ASRC_WIDTH_16_BIT; + break; + case 24: + output_word_width = ASRC_WIDTH_24_BIT; + break; + default: + pair_err("does not support this output format, %d\n", + config->output_format); return -EINVAL; } @@ -326,27 +359,42 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) /* We only have output clock for ideal ratio mode */ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; - div[IN] = clk_get_rate(clk) / inrate; - if (div[IN] == 0) { + clk_rate = clk_get_rate(clk); + rem[IN] = do_div(clk_rate, inrate); + div[IN] = (u32)clk_rate; + + /* + * The divider range is [1, 1024], defined by the hardware. For non- + * ideal ratio configuration, clock rate has to be strictly aligned + * with the sample rate. For ideal ratio configuration, clock rates + * only result in different converting speeds. So remainder does not + * matter, as long as we keep the divider within its valid range. + */ + if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; } - clk = asrc_priv->asrck_clk[clk_index[OUT]]; + div[IN] = min_t(u32, 1024, div[IN]); - /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ - if (ideal) - div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; + clk = asrc_priv->asrck_clk[clk_index[OUT]]; + clk_rate = clk_get_rate(clk); + if (ideal && use_ideal_rate) + rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE); else - div[OUT] = clk_get_rate(clk) / outrate; + rem[OUT] = do_div(clk_rate, outrate); + div[OUT] = clk_rate; - if (div[OUT] == 0) { + /* Output divider has the same limitation as the input one */ + if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; } + div[OUT] = min_t(u32, 1024, div[OUT]); + /* Set the channel number */ channels = config->channel_num; @@ -383,8 +431,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) /* Implement word_width configurations */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index), ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK, - ASRMCR1i_OW16(config->output_word_width) | - ASRMCR1i_IWD(config->input_word_width)); + ASRMCR1i_OW16(output_word_width) | + ASRMCR1i_IWD(input_word_width)); /* Enable BUFFER STALL */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), @@ -497,13 +545,13 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); - int width = params_width(params); struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); struct asrc_config config; - int word_width, ret; + snd_pcm_format_t format; + int ret; ret = fsl_asrc_request_pair(channels, pair); if (ret) { @@ -513,15 +561,10 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, pair->config = &config; - if (width == 16) - width = ASRC_WIDTH_16_BIT; - else - width = ASRC_WIDTH_24_BIT; - if (asrc_priv->asrc_width == 16) - word_width = ASRC_WIDTH_16_BIT; + format = SNDRV_PCM_FORMAT_S16_LE; else - word_width = ASRC_WIDTH_24_BIT; + format = SNDRV_PCM_FORMAT_S24_LE; config.pair = pair->index; config.channel_num = channels; @@ -529,18 +572,18 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, config.outclk = OUTCLK_ASRCK1_CLK; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - config.input_word_width = width; - config.output_word_width = word_width; + config.input_format = params_format(params); + config.output_format = format; config.input_sample_rate = rate; config.output_sample_rate = asrc_priv->asrc_rate; } else { - config.input_word_width = word_width; - config.output_word_width = width; + config.input_format = format; + config.output_format = params_format(params); config.input_sample_rate = asrc_priv->asrc_rate; config.output_sample_rate = rate; } - ret = fsl_asrc_config_pair(pair); + ret = fsl_asrc_config_pair(pair, false); if (ret) { dev_err(dai->dev, "fail to config asrc pair\n"); return ret; @@ -604,7 +647,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE) + SNDRV_PCM_FMTBIT_S24_3LE) static struct snd_soc_dai_driver fsl_asrc_dai = { .probe = fsl_asrc_dai_probe, @@ -615,7 +658,8 @@ static struct snd_soc_dai_driver fsl_asrc_dai = { .rate_min = 5512, .rate_max = 192000, .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_ASRC_FORMATS, + .formats = FSL_ASRC_FORMATS | + SNDRV_PCM_FMTBIT_S8, }, .capture = { .stream_name = "ASRC-Capture", diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index c60075112570..2b57e8c53728 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -342,8 +342,8 @@ struct asrc_config { unsigned int dma_buffer_size; unsigned int input_sample_rate; unsigned int output_sample_rate; - enum asrc_word_width input_word_width; - enum asrc_word_width output_word_width; + snd_pcm_format_t input_format; + snd_pcm_format_t output_format; enum asrc_inclk inclk; enum asrc_outclk outclk; }; @@ -462,4 +462,7 @@ struct fsl_asrc { #define DRV_NAME "fsl-asrc-dai" extern struct snd_soc_component_driver fsl_asrc_component; struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir); +int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair); +void fsl_asrc_release_pair(struct fsl_asrc_pair *pair); + #endif /* _FSL_ASRC_H */ diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 01052a0808b0..d6146de9acd2 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -16,13 +16,11 @@ #define FSL_ASRC_DMABUF_SIZE (256 * 1024) -static const struct snd_pcm_hardware snd_imx_hardware = { +static struct snd_pcm_hardware snd_imx_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, + SNDRV_PCM_INFO_MMAP_VALID, .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 65535, /* Limited by SDMA engine */ @@ -54,13 +52,12 @@ static void fsl_asrc_dma_complete(void *arg) snd_pcm_period_elapsed(substream); } -static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream) +static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream, + struct snd_soc_component *component) { u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; unsigned long flags = DMA_CTRL_ACK; @@ -97,7 +94,8 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream) return 0; } -static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd) +static int fsl_asrc_dma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; @@ -107,7 +105,7 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = fsl_asrc_dma_prepare_and_submit(substream); + ret = fsl_asrc_dma_prepare_and_submit(substream, component); if (ret) return ret; dma_async_issue_pending(pair->dma_chan[IN]); @@ -126,7 +124,8 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, +static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; @@ -134,7 +133,6 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL; struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; struct fsl_asrc *asrc_priv = pair->asrc_priv; @@ -249,7 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, return 0; } -static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream) +static int fsl_asrc_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; @@ -268,14 +267,27 @@ static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream) return 0; } -static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream) +static int fsl_asrc_dma_startup(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_dmaengine_dai_dma_data *dma_data; struct device *dev = component->dev; struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); struct fsl_asrc_pair *pair; + struct dma_chan *tmp_chan = NULL; + u8 dir = tx ? OUT : IN; + bool release_pair = true; + int ret = 0; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(dev, "failed to set pcm hw params periods\n"); + return ret; + } pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL); if (!pair) @@ -285,14 +297,54 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream) runtime->private_data = pair; - snd_pcm_hw_constraint_integer(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIODS); + /* Request a dummy pair, which will be released later. + * Request pair function needs channel num as input, for this + * dummy pair, we just request "1" channel temporarily. + */ + ret = fsl_asrc_request_pair(1, pair); + if (ret < 0) { + dev_err(dev, "failed to request asrc pair\n"); + goto req_pair_err; + } + + /* Request a dummy dma channel, which will be released later. */ + tmp_chan = fsl_asrc_get_dma_channel(pair, dir); + if (!tmp_chan) { + dev_err(dev, "failed to get dma channel\n"); + ret = -EINVAL; + goto dma_chan_err; + } + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + /* Refine the snd_imx_hardware according to caps of DMA. */ + ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, + dma_data, + &snd_imx_hardware, + tmp_chan); + if (ret < 0) { + dev_err(dev, "failed to refine runtime hwparams\n"); + goto out; + } + + release_pair = false; snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); - return 0; +out: + dma_release_channel(tmp_chan); + +dma_chan_err: + fsl_asrc_release_pair(pair); + +req_pair_err: + if (release_pair) + kfree(pair); + + return ret; } -static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream) +static int fsl_asrc_dma_shutdown(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; @@ -311,7 +363,9 @@ static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream) return 0; } -static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t +fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; @@ -319,17 +373,8 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs return bytes_to_frames(substream->runtime, pair->pos); } -static const struct snd_pcm_ops fsl_asrc_dma_pcm_ops = { - .ioctl = snd_pcm_lib_ioctl, - .hw_params = fsl_asrc_dma_hw_params, - .hw_free = fsl_asrc_dma_hw_free, - .trigger = fsl_asrc_dma_trigger, - .open = fsl_asrc_dma_startup, - .close = fsl_asrc_dma_shutdown, - .pointer = fsl_asrc_dma_pcm_pointer, -}; - -static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm_substream *substream; @@ -364,7 +409,8 @@ err: return ret; } -static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm) +static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; int i; @@ -382,8 +428,14 @@ static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm) struct snd_soc_component_driver fsl_asrc_component = { .name = DRV_NAME, - .ops = &fsl_asrc_dma_pcm_ops, - .pcm_new = fsl_asrc_dma_pcm_new, - .pcm_free = fsl_asrc_dma_pcm_free, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = fsl_asrc_dma_hw_params, + .hw_free = fsl_asrc_dma_hw_free, + .trigger = fsl_asrc_dma_trigger, + .open = fsl_asrc_dma_startup, + .close = fsl_asrc_dma_shutdown, + .pointer = fsl_asrc_dma_pcm_pointer, + .pcm_construct = fsl_asrc_dma_pcm_new, + .pcm_destruct = fsl_asrc_dma_pcm_free, }; EXPORT_SYMBOL_GPL(fsl_asrc_component); diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index c7e4e9757dce..a1db1bce330f 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -286,6 +286,7 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); + unsigned long lock_flags; /* Capture stream shall not be handled */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -295,12 +296,16 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&priv->lock, lock_flags); priv->tdms |= BIT(dai->driver->id); + spin_unlock_irqrestore(&priv->lock, lock_flags); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&priv->lock, lock_flags); priv->tdms &= ~BIT(dai->driver->id); + spin_unlock_irqrestore(&priv->lock, lock_flags); break; default: return -EINVAL; @@ -491,6 +496,7 @@ static int fsl_audmix_probe(struct platform_device *pdev) return PTR_ERR(priv->ipg_clk); } + spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv); pm_runtime_enable(dev); diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h index 7812ffec45c5..479f05695d53 100644 --- a/sound/soc/fsl/fsl_audmix.h +++ b/sound/soc/fsl/fsl_audmix.h @@ -96,6 +96,7 @@ struct fsl_audmix { struct platform_device *pdev; struct regmap *regmap; struct clk *ipg_clk; + spinlock_t lock; /* Protect tdms */ u8 tdms; }; diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index e22508301412..2868c4f97cb2 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -201,8 +201,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) struct fsl_dma_private *dma_private = dev_id; struct snd_pcm_substream *substream = dma_private->substream; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct device *dev = component->dev; + struct device *dev = rtd->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; irqreturn_t ret = IRQ_NONE; u32 sr, sr2 = 0; @@ -280,7 +279,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) * Regardless of where the memory is actually allocated, since the device can * technically DMA to any 36-bit address, we do need to set the DMA mask to 36. */ -static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd) +static int fsl_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -380,11 +380,10 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd) * buffer, which is what ALSA expects. We're just dividing it into * contiguous parts, and creating a link descriptor for each one. */ -static int fsl_dma_open(struct snd_pcm_substream *substream) +static int fsl_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; struct dma_object *dma = container_of(component->driver, struct dma_object, dai); @@ -533,13 +532,12 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) * and 8 bytes at a time). So we do not support packed 24-bit samples. * 24-bit data must be padded to 32 bits. */ -static int fsl_dma_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int fsl_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; /* Number of bits per sample */ @@ -698,12 +696,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, * The base address of the buffer is stored in the source_addr field of the * first link descriptor. */ -static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; dma_addr_t position; @@ -763,7 +760,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) * * This function can be called multiple times. */ -static int fsl_dma_hw_free(struct snd_pcm_substream *substream) +static int fsl_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; @@ -796,12 +794,11 @@ static int fsl_dma_hw_free(struct snd_pcm_substream *substream) /** * fsl_dma_close: close the stream. */ -static int fsl_dma_close(struct snd_pcm_substream *substream) +static int fsl_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_dma_private *dma_private = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; struct dma_object *dma = container_of(component->driver, struct dma_object, dai); @@ -824,7 +821,8 @@ static int fsl_dma_close(struct snd_pcm_substream *substream) /* * Remove this PCM driver. */ -static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) +static void fsl_dma_free_dma_buffers(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; unsigned int i; @@ -872,15 +870,6 @@ static struct device_node *find_ssi_node(struct device_node *dma_channel_np) return NULL; } -static const struct snd_pcm_ops fsl_dma_ops = { - .open = fsl_dma_open, - .close = fsl_dma_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = fsl_dma_hw_params, - .hw_free = fsl_dma_hw_free, - .pointer = fsl_dma_pointer, -}; - static int fsl_soc_dma_probe(struct platform_device *pdev) { struct dma_object *dma; @@ -912,9 +901,14 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) } dma->dai.name = DRV_NAME; - dma->dai.ops = &fsl_dma_ops; - dma->dai.pcm_new = fsl_dma_new; - dma->dai.pcm_free = fsl_dma_free_dma_buffers; + dma->dai.open = fsl_dma_open; + dma->dai.close = fsl_dma_close; + dma->dai.ioctl = snd_soc_pcm_lib_ioctl; + dma->dai.hw_params = fsl_dma_hw_params; + dma->dai.hw_free = fsl_dma_hw_free; + dma->dai.pointer = fsl_dma_pointer; + dma->dai.pcm_construct = fsl_dma_new; + dma->dai.pcm_destruct = fsl_dma_free_dma_buffers; /* Store the SSI-specific information that we need */ dma->ssi_stx_phys = res.start + REG_SSI_STX0; diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index a78e4ab478df..c7a49d03463a 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -33,6 +33,7 @@ * @fsysclk: system clock source to derive HCK, SCK and FS * @spbaclk: SPBA clock (optional, depending on SoC design) * @task: tasklet to handle the reset operation + * @lock: spin lock between hw_reset() and trigger() * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots @@ -56,6 +57,7 @@ struct fsl_esai { struct clk *fsysclk; struct clk *spbaclk; struct tasklet_struct task; + spinlock_t lock; /* Protect hw_reset and trigger */ u32 fifo_depth; u32 slot_width; u32 slots; @@ -676,8 +678,10 @@ static void fsl_esai_hw_reset(unsigned long arg) { struct fsl_esai *esai_priv = (struct fsl_esai *)arg; bool tx = true, rx = false, enabled[2]; + unsigned long lock_flags; u32 tfcr, rfcr; + spin_lock_irqsave(&esai_priv->lock, lock_flags); /* Save the registers */ regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); @@ -715,6 +719,8 @@ static void fsl_esai_hw_reset(unsigned long arg) fsl_esai_trigger_start(esai_priv, tx); if (enabled[rx]) fsl_esai_trigger_start(esai_priv, rx); + + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); } static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -722,6 +728,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned long lock_flags; esai_priv->channels[tx] = substream->runtime->channels; @@ -729,12 +736,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&esai_priv->lock, lock_flags); fsl_esai_trigger_start(esai_priv, tx); + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&esai_priv->lock, lock_flags); fsl_esai_trigger_stop(esai_priv, tx); + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); break; default: return -EINVAL; @@ -1002,6 +1013,7 @@ static int fsl_esai_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, esai_priv); + spin_lock_init(&esai_priv->lock); ret = fsl_esai_hw_init(esai_priv); if (ret) return ret; diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c new file mode 100644 index 000000000000..0c813a45bba7 --- /dev/null +++ b/sound/soc/fsl/fsl_mqs.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC IMX MQS driver +// +// Copyright (C) 2014-2015 Freescale Semiconductor, Inc. +// Copyright 2019 NXP + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/initval.h> + +#define REG_MQS_CTRL 0x00 + +#define MQS_EN_MASK (0x1 << 28) +#define MQS_EN_SHIFT (28) +#define MQS_SW_RST_MASK (0x1 << 24) +#define MQS_SW_RST_SHIFT (24) +#define MQS_OVERSAMPLE_MASK (0x1 << 20) +#define MQS_OVERSAMPLE_SHIFT (20) +#define MQS_CLK_DIV_MASK (0xFF << 0) +#define MQS_CLK_DIV_SHIFT (0) + +/* codec private data */ +struct fsl_mqs { + struct regmap *regmap; + struct clk *mclk; + struct clk *ipg; + + unsigned int reg_iomuxc_gpr2; + unsigned int reg_mqs_ctrl; + bool use_gpr; +}; + +#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static int fsl_mqs_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 fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component); + unsigned long mclk_rate; + int div, res; + int lrclk; + + mclk_rate = clk_get_rate(mqs_priv->mclk); + lrclk = params_rate(params); + + /* + * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate; + * if repeat_rate is 8, mqs can achieve better quality. + * oversample rate is fix to 32 currently. + */ + div = mclk_rate / (32 * lrclk * 2 * 8); + res = mclk_rate % (32 * lrclk * 2 * 8); + + if (res == 0 && div > 0 && div <= 256) { + if (mqs_priv->use_gpr) { + regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_CLK_DIV_MASK, + (div - 1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT); + regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0); + } else { + regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL, + MQS_CLK_DIV_MASK, + (div - 1) << MQS_CLK_DIV_SHIFT); + regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL, + MQS_OVERSAMPLE_MASK, 0); + } + } else { + dev_err(component->dev, "can't get proper divider\n"); + } + + return 0; +} + +static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /* Only LEFT_J & SLAVE mode is supported. */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_mqs_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component); + + if (mqs_priv->use_gpr) + regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_EN_MASK, + 1 << IMX6SX_GPR2_MQS_EN_SHIFT); + else + regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL, + MQS_EN_MASK, + 1 << MQS_EN_SHIFT); + return 0; +} + +static void fsl_mqs_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component); + + if (mqs_priv->use_gpr) + regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2, + IMX6SX_GPR2_MQS_EN_MASK, 0); + else + regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL, + MQS_EN_MASK, 0); +} + +static const struct snd_soc_component_driver soc_codec_fsl_mqs = { + .idle_bias_on = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_dai_ops fsl_mqs_dai_ops = { + .startup = fsl_mqs_startup, + .shutdown = fsl_mqs_shutdown, + .hw_params = fsl_mqs_hw_params, + .set_fmt = fsl_mqs_set_dai_fmt, +}; + +static struct snd_soc_dai_driver fsl_mqs_dai = { + .name = "fsl-mqs-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = FSL_MQS_RATES, + .formats = FSL_MQS_FORMATS, + }, + .ops = &fsl_mqs_dai_ops, +}; + +static const struct regmap_config fsl_mqs_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = REG_MQS_CTRL, + .cache_type = REGCACHE_NONE, +}; + +static int fsl_mqs_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *gpr_np = NULL; + struct fsl_mqs *mqs_priv; + void __iomem *regs; + int ret; + + mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL); + if (!mqs_priv) + return -ENOMEM; + + /* On i.MX6sx the MQS control register is in GPR domain + * But in i.MX8QM/i.MX8QXP the control register is moved + * to its own domain. + */ + if (of_device_is_compatible(np, "fsl,imx8qm-mqs")) + mqs_priv->use_gpr = false; + else + mqs_priv->use_gpr = true; + + if (mqs_priv->use_gpr) { + gpr_np = of_parse_phandle(np, "gpr", 0); + if (!gpr_np) { + dev_err(&pdev->dev, "failed to get gpr node by phandle\n"); + return -EINVAL; + } + + mqs_priv->regmap = syscon_node_to_regmap(gpr_np); + if (IS_ERR(mqs_priv->regmap)) { + dev_err(&pdev->dev, "failed to get gpr regmap\n"); + ret = PTR_ERR(mqs_priv->regmap); + goto err_free_gpr_np; + } + } else { + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "core", + regs, + &fsl_mqs_regmap_config); + if (IS_ERR(mqs_priv->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(mqs_priv->regmap)); + return PTR_ERR(mqs_priv->regmap); + } + + mqs_priv->ipg = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(mqs_priv->ipg)) { + dev_err(&pdev->dev, "failed to get the clock: %ld\n", + PTR_ERR(mqs_priv->ipg)); + return PTR_ERR(mqs_priv->ipg); + } + } + + mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mqs_priv->mclk)) { + dev_err(&pdev->dev, "failed to get the clock: %ld\n", + PTR_ERR(mqs_priv->mclk)); + ret = PTR_ERR(mqs_priv->mclk); + goto err_free_gpr_np; + } + + dev_set_drvdata(&pdev->dev, mqs_priv); + pm_runtime_enable(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs, + &fsl_mqs_dai, 1); + if (ret) + goto err_free_gpr_np; + return 0; + +err_free_gpr_np: + of_node_put(gpr_np); + + return ret; +} + +static int fsl_mqs_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM +static int fsl_mqs_runtime_resume(struct device *dev) +{ + struct fsl_mqs *mqs_priv = dev_get_drvdata(dev); + + if (mqs_priv->ipg) + clk_prepare_enable(mqs_priv->ipg); + + if (mqs_priv->mclk) + clk_prepare_enable(mqs_priv->mclk); + + if (mqs_priv->use_gpr) + regmap_write(mqs_priv->regmap, IOMUXC_GPR2, + mqs_priv->reg_iomuxc_gpr2); + else + regmap_write(mqs_priv->regmap, REG_MQS_CTRL, + mqs_priv->reg_mqs_ctrl); + return 0; +} + +static int fsl_mqs_runtime_suspend(struct device *dev) +{ + struct fsl_mqs *mqs_priv = dev_get_drvdata(dev); + + if (mqs_priv->use_gpr) + regmap_read(mqs_priv->regmap, IOMUXC_GPR2, + &mqs_priv->reg_iomuxc_gpr2); + else + regmap_read(mqs_priv->regmap, REG_MQS_CTRL, + &mqs_priv->reg_mqs_ctrl); + + if (mqs_priv->mclk) + clk_disable_unprepare(mqs_priv->mclk); + + if (mqs_priv->ipg) + clk_disable_unprepare(mqs_priv->ipg); + + return 0; +} +#endif + +static const struct dev_pm_ops fsl_mqs_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend, + fsl_mqs_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id fsl_mqs_dt_ids[] = { + { .compatible = "fsl,imx8qm-mqs", }, + { .compatible = "fsl,imx6sx-mqs", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids); + +static struct platform_driver fsl_mqs_driver = { + .probe = fsl_mqs_probe, + .remove = fsl_mqs_remove, + .driver = { + .name = "fsl-mqs", + .of_match_table = fsl_mqs_dt_ids, + .pm = &fsl_mqs_pm_ops, + }, +}; + +module_platform_driver(fsl_mqs_driver); + +MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>"); +MODULE_DESCRIPTION("MQS codec driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform: fsl-mqs"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index c49aea4fba56..08131d147983 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -69,8 +69,9 @@ static struct fiq_handler fh = { .name = DRV_NAME, }; -static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int snd_imx_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; @@ -85,7 +86,8 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) +static int snd_imx_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; @@ -104,7 +106,8 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) static int imx_pcm_fiq; -static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int snd_imx_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; @@ -141,7 +144,9 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t +snd_imx_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; @@ -165,7 +170,8 @@ static const struct snd_pcm_hardware snd_imx_hardware = { .fifo_size = 0, }; -static int snd_imx_open(struct snd_pcm_substream *substream) +static int snd_imx_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd; @@ -194,7 +200,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream) return 0; } -static int snd_imx_close(struct snd_pcm_substream *substream) +static int snd_imx_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; @@ -206,8 +213,9 @@ static int snd_imx_close(struct snd_pcm_substream *substream) return 0; } -static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int snd_imx_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -222,17 +230,6 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, return ret; } -static const struct snd_pcm_ops imx_pcm_ops = { - .open = snd_imx_open, - .close = snd_imx_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_imx_pcm_hw_params, - .prepare = snd_imx_pcm_prepare, - .trigger = snd_imx_pcm_trigger, - .pointer = snd_imx_pcm_pointer, - .mmap = snd_imx_pcm_mmap, -}; - static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; @@ -279,7 +276,8 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) static int ssi_irq; -static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) +static int snd_imx_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; struct snd_pcm_substream *substream; @@ -329,7 +327,8 @@ static void imx_pcm_free(struct snd_pcm *pcm) } } -static void imx_pcm_fiq_free(struct snd_pcm *pcm) +static void snd_imx_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { mxc_set_irq_fiq(ssi_irq, 0); release_fiq(&fh); @@ -337,9 +336,16 @@ static void imx_pcm_fiq_free(struct snd_pcm *pcm) } static const struct snd_soc_component_driver imx_soc_component_fiq = { - .ops = &imx_pcm_ops, - .pcm_new = imx_pcm_fiq_new, - .pcm_free = imx_pcm_fiq_free, + .open = snd_imx_open, + .close = snd_imx_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = snd_imx_pcm_hw_params, + .prepare = snd_imx_pcm_prepare, + .trigger = snd_imx_pcm_trigger, + .pointer = snd_imx_pcm_pointer, + .mmap = snd_imx_pcm_mmap, + .pcm_construct = snd_imx_pcm_new, + .pcm_destruct = snd_imx_pcm_free, }; int imx_pcm_fiq_init(struct platform_device *pdev, diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index ccf9301889fe..5237ac96b756 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -98,7 +98,8 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) return IRQ_HANDLED; } -static int psc_dma_hw_free(struct snd_pcm_substream *substream) +static int psc_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -110,7 +111,8 @@ static int psc_dma_hw_free(struct snd_pcm_substream *substream) * This function is called by ALSA to start, stop, pause, and resume the DMA * transfer of data. */ -static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) +static int psc_dma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); @@ -210,7 +212,8 @@ static const struct snd_pcm_hardware psc_dma_hardware = { .fifo_size = 512, }; -static int psc_dma_open(struct snd_pcm_substream *substream) +static int psc_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -238,7 +241,8 @@ static int psc_dma_open(struct snd_pcm_substream *substream) return 0; } -static int psc_dma_close(struct snd_pcm_substream *substream) +static int psc_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); @@ -263,7 +267,8 @@ static int psc_dma_close(struct snd_pcm_substream *substream) } static snd_pcm_uframes_t -psc_dma_pointer(struct snd_pcm_substream *substream) +psc_dma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); @@ -280,29 +285,19 @@ psc_dma_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, count); } -static int -psc_dma_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int psc_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); return 0; } -static const struct snd_pcm_ops psc_dma_ops = { - .open = psc_dma_open, - .close = psc_dma_close, - .hw_free = psc_dma_hw_free, - .ioctl = snd_pcm_lib_ioctl, - .pointer = psc_dma_pointer, - .trigger = psc_dma_trigger, - .hw_params = psc_dma_hw_params, -}; - -static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) +static int psc_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; size_t size = psc_dma_hardware.buffer_bytes_max; @@ -341,10 +336,10 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) return -ENOMEM; } -static void psc_dma_free(struct snd_pcm *pcm) +static void psc_dma_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_pcm_substream *substream; int stream; @@ -362,9 +357,15 @@ static void psc_dma_free(struct snd_pcm *pcm) static const struct snd_soc_component_driver mpc5200_audio_dma_component = { .name = DRV_NAME, - .ops = &psc_dma_ops, - .pcm_new = &psc_dma_new, - .pcm_free = &psc_dma_free, + .open = psc_dma_open, + .close = psc_dma_close, + .hw_free = psc_dma_hw_free, + .ioctl = snd_soc_pcm_lib_ioctl, + .pointer = psc_dma_pointer, + .trigger = psc_dma_trigger, + .hw_params = psc_dma_hw_params, + .pcm_construct = psc_dma_new, + .pcm_destruct = psc_dma_free, }; int mpc5200_audio_dma_create(struct platform_device *op) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 6007e6305735..9ad35d9940fe 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -232,7 +232,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, if (li->cpu) { int is_single_links = 0; - /* BE is dummy */ + /* Codec is dummy */ codecs->of_node = NULL; codecs->dai_name = "snd-soc-dummy-dai"; codecs->name = "snd-soc-dummy"; @@ -263,7 +263,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, } else { struct snd_soc_codec_conf *cconf; - /* FE is dummy */ + /* CPU is dummy */ cpus->of_node = NULL; cpus->dai_name = "snd-soc-dummy-dai"; cpus->name = "snd-soc-dummy"; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fc9c753db8dd..10b82bf043d1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -149,7 +149,7 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, if (li->cpu) { int is_single_links = 0; - /* BE is dummy */ + /* Codec is dummy */ codecs->of_node = NULL; codecs->dai_name = "snd-soc-dummy-dai"; codecs->name = "snd-soc-dummy"; @@ -179,7 +179,7 @@ static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv, } else { struct snd_soc_codec_conf *cconf; - /* FE is dummy */ + /* CPU is dummy */ cpus->of_node = NULL; cpus->dai_name = "snd-soc-dummy-dai"; cpus->name = "snd-soc-dummy"; diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 01c99750212a..79b227613108 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -59,10 +59,13 @@ config SND_SOC_INTEL_HASWELL If you have a Intel Haswell or Broadwell platform connected to an I2S codec, then enable this option by saying Y or m. This is typically used for Chromebooks. This is a recommended option. + This option is mutually exclusive with the SOF support on + Broadwell. If you want to enable SOF on Broadwell, you need to + deselect this option first. config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -101,6 +104,9 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI If you have a Intel Baytrail or Cherrytrail platform with an I2S codec, then enable this option by saying Y or m. This is a recommended option + This option is mutually exclusive with the SOF support on + Baytrail/Cherrytrail. If you want to enable SOF on + Baytrail/Cherrytrail, you need to deselect this option first. config SND_SOC_INTEL_SKYLAKE tristate "All Skylake/SST Platforms" @@ -203,9 +209,12 @@ config SND_SOC_INTEL_SKYLAKE_SSP_CLK config SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC bool "HDAudio codec support" help - If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ - GeminiLake or CannonLake platform with an HDaudio codec - then enable this option by saying Y + This option broke audio on Linus' Skylake laptop in December 2018 + and the race conditions during the probe were not fixed since. + This option is DEPRECATED, all HDaudio codec support needs + to be handled by the SOF driver. + Distributions should not enable this option and there are no known + users of this capability. config SND_SOC_INTEL_SKYLAKE_COMMON tristate diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 8cc3cc363eb0..47e3d1943d7e 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -586,7 +586,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, }; -static int sst_platform_open(struct snd_pcm_substream *substream) +static int sst_soc_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; @@ -598,15 +599,15 @@ static int sst_platform_open(struct snd_pcm_substream *substream) return 0; } -static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) +static int sst_soc_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { int ret_val = 0, str_id; struct sst_runtime_stream *stream; int status; struct snd_soc_pcm_runtime *rtd = substream->private_data; - dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n"); + dev_dbg(rtd->dev, "%s called\n", __func__); if (substream->pcm->internal) return 0; stream = substream->runtime->private_data; @@ -646,8 +647,8 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, } -static snd_pcm_uframes_t sst_platform_pcm_pointer - (struct snd_pcm_substream *substream) +static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct sst_runtime_stream *stream; int ret_val, status; @@ -668,14 +669,8 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer return str_info->buffer_ptr; } -static const struct snd_pcm_ops sst_platform_ops = { - .open = sst_platform_open, - .ioctl = snd_pcm_lib_ioctl, - .trigger = sst_platform_pcm_trigger, - .pointer = sst_platform_pcm_pointer, -}; - -static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int sst_soc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; @@ -709,9 +704,12 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .name = DRV_NAME, .probe = sst_soc_probe, .remove = sst_soc_remove, - .ops = &sst_platform_ops, + .open = sst_soc_open, + .ioctl = snd_soc_pcm_lib_ioctl, + .trigger = sst_soc_trigger, + .pointer = sst_soc_pointer, .compr_ops = &sst_platform_compr_ops, - .pcm_new = sst_pcm_new, + .pcm_construct = sst_soc_pcm_new, }; static int sst_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index 54f2ee3010ee..1d780fcc448c 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -58,11 +58,11 @@ struct sst_byt_priv_data { }; /* this may get called several times by oss emulation */ -static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream, +static int sst_byt_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; struct sst_byt *byt = pdata->byt; @@ -121,7 +121,8 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream) +static int sst_byt_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -164,10 +165,10 @@ static void sst_byt_pcm_work(struct work_struct *work) sst_byt_pcm_restore_stream_context(pcm_data->substream); } -static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int sst_byt_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; struct sst_byt *byt = pdata->byt; @@ -228,11 +229,11 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data) return pos; } -static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; @@ -241,10 +242,10 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream return bytes_to_frames(runtime, pcm_data->hw_ptr); } -static int sst_byt_pcm_open(struct snd_pcm_substream *substream) +static int sst_byt_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; struct sst_byt *byt = pdata->byt; @@ -269,10 +270,10 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int sst_byt_pcm_close(struct snd_pcm_substream *substream) +static int sst_byt_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; struct sst_byt *byt = pdata->byt; @@ -294,7 +295,8 @@ out: return ret; } -static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream, +static int sst_byt_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -303,22 +305,11 @@ static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream, return snd_pcm_lib_default_mmap(substream, vma); } -static const struct snd_pcm_ops sst_byt_pcm_ops = { - .open = sst_byt_pcm_open, - .close = sst_byt_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = sst_byt_pcm_hw_params, - .hw_free = sst_byt_pcm_hw_free, - .trigger = sst_byt_pcm_trigger, - .pointer = sst_byt_pcm_pointer, - .mmap = sst_byt_pcm_mmap, -}; - -static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int sst_byt_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; size_t size; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_pdata *pdata = dev_get_platdata(component->dev); if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || @@ -380,8 +371,15 @@ static int sst_byt_pcm_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver byt_dai_component = { .name = DRV_NAME, .probe = sst_byt_pcm_probe, - .ops = &sst_byt_pcm_ops, - .pcm_new = sst_byt_pcm_new, + .open = sst_byt_pcm_open, + .close = sst_byt_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = sst_byt_pcm_hw_params, + .hw_free = sst_byt_pcm_hw_free, + .trigger = sst_byt_pcm_trigger, + .pointer = sst_byt_pcm_pointer, + .mmap = sst_byt_pcm_mmap, + .pcm_construct = sst_byt_pcm_new, }; #ifdef CONFIG_PM diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 5c27f7ab4a5f..6c9fd9ad566e 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -263,14 +263,17 @@ config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC select SND_SOC_DMIC select SND_SOC_HDAC_HDMI +config SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON + tristate + select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC + if SND_SOC_INTEL_APL config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST - select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC - select SND_HDA_DSP_LOADER + select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON help This adds support for ASoC machine driver for Broxton-P platforms with DA7219 + MAX98357A I2S audio codec. @@ -284,7 +287,6 @@ config SND_SOC_INTEL_BXT_RT298_MACH select SND_SOC_RT298 select SND_SOC_DMIC select SND_SOC_HDAC_HDMI - select SND_HDA_DSP_LOADER help This adds support for ASoC machine driver for Broxton platforms with RT286 I2S audio codec. @@ -320,6 +322,7 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH select SND_SOC_RT5514_SPI select SND_SOC_MAX98927 select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_SKYLAKE_SSP_CLK help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for RT5663 + RT5514 + MAX98927. @@ -364,7 +367,18 @@ config SND_SOC_INTEL_KBL_RT5660_MACH endif ## SND_SOC_INTEL_KBL -if SND_SOC_INTEL_GLK || (SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK) +if SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK + +config SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH + tristate "GLK with DA7219 and MAX98357A in I2S Mode" + depends on I2C && ACPI + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON + help + This adds support for ASoC machine driver for Geminilake platforms + with DA7219 + MAX98357A I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" @@ -374,14 +388,13 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH select SND_SOC_MAX98357A select SND_SOC_DMIC select SND_SOC_HDAC_HDMI - select SND_HDA_DSP_LOADER help This adds support for ASoC machine driver for Geminilake platforms with RT5682 + MAX98357A I2S audio codec. Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_GLK || (SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK) +endif ## SND_SOC_SOF_GEMINILAKE && SND_SOC_SOF_HDA_LINK if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC @@ -398,11 +411,11 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC -if SND_SOC_SOF_HDA_COMMON || SND_SOC_SOF_BAYTRAIL +if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_SOF_RT5682_MACH tristate "SOF with rt5682 codec in I2S Mode" depends on I2C && ACPI - depends on (SND_SOC_SOF_HDA_COMMON && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ + depends on (SND_SOC_SOF_HDA_LINK && (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_RT5682 select SND_SOC_DMIC @@ -412,7 +425,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH with rt5682 codec. Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_SOF_HDA_COMMON || SND_SOC_SOF_BAYTRAIL +endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL if (SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK) @@ -420,7 +433,26 @@ config SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH tristate "CML_LP with DA7219 and MAX98357A in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST - select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC + select SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON + help + This adds support for ASoC machine driver for Cometlake platforms + with DA7219 + MAX98357A I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + +config SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH + tristate "CML with RT1011 and RT5682 in I2S Mode" + depends on I2C && ACPI + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT1011 + select SND_SOC_RT5682 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for SOF platform with + RT1011 + RT5682 I2S codec. + Say Y if you have such a device. + If unsure select "N". endif ## SND_SOC_SOF_COMETLAKE_LP && SND_SOC_SOF_HDA_LINK diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 6445f90ea542..ba1aa89db09d 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -4,9 +4,9 @@ snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o -snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o -snd-soc-sst-bxt-rt298-objs := bxt_rt298.o -snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o +snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o hda_dsp_common.o +snd-soc-sst-bxt-rt298-objs := bxt_rt298.o hda_dsp_common.o +snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o hda_dsp_common.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -17,14 +17,15 @@ snd-soc-sst-byt-cht-cx2072x-objs := bytcht_cx2072x.o snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o -snd-soc-sof_rt5682-objs := sof_rt5682.o +snd-soc-sof_rt5682-objs := sof_rt5682.o hda_dsp_common.o +snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o hda_dsp_common.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-kbl_rt5660-objs := kbl_rt5660.o snd-soc-skl_rt286-objs := skl_rt286.o -snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o +snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o @@ -32,7 +33,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o -obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o @@ -47,6 +48,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x. obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH) += snd-soc-cml_rt1011_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 4a4d3353e26d..2af8e5a62da8 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -74,6 +74,11 @@ static const struct snd_soc_dapm_route bdw_rt5677_map[] = { /* CODEC BE connections */ {"SSP0 CODEC IN", NULL, "AIF1 Capture"}, {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, + {"DSP Capture", NULL, "DSP Buffer"}, + + /* DSP Clock Connections */ + { "DSP Buffer", NULL, "SSP0 CODEC IN" }, + { "SSP0 CODEC IN", NULL, "DSPTX" }, }; static const struct snd_kcontrol_new bdw_rt5677_controls[] = { @@ -165,10 +170,37 @@ static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream, return ret; } +static int bdw_rt5677_dsp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_PLL1, 24576000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5677_PLL1_S_MCLK, + 24000000, 24576000); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll configuration\n"); + return ret; + } + + return 0; +} + static const struct snd_soc_ops bdw_rt5677_ops = { .hw_params = bdw_rt5677_hw_params, }; +static const struct snd_soc_ops bdw_rt5677_dsp_ops = { + .hw_params = bdw_rt5677_dsp_hw_params, +}; + #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) { @@ -208,6 +240,11 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) rt5677_sel_asrc_clk_src(component, RT5677_DA_STEREO_FILTER | RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE, RT5677_CLK_SEL_I2S1_ASRC); + /* Enable codec ASRC function for Mono ADC L. + * The ASRC clock source is clk_sys2_asrc. + */ + rt5677_sel_asrc_clk_src(component, RT5677_AD_MONO_L_FILTER, + RT5677_CLK_SEL_SYS2); /* Request rt5677 GPIO for headphone amp control */ bdw_rt5677->gpio_hp_en = devm_gpiod_get(component->dev, "headphone-enable", @@ -258,6 +295,12 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(be, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-aif1"))); +/* Wake on voice interface */ +SND_SOC_DAILINK_DEFS(dsp, + DAILINK_COMP_ARRAY(COMP_CPU("spi-RT5677AA:00")), + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-dspbuffer")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("spi-RT5677AA:00"))); + static struct snd_soc_dai_link bdw_rt5677_dais[] = { /* Front End DAI links */ { @@ -276,6 +319,14 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { SND_SOC_DAILINK_REG(fe, dummy, platform), }, + /* Non-DPCM links */ + { + .name = "Codec DSP", + .stream_name = "Wake on Voice", + .ops = &bdw_rt5677_dsp_ops, + SND_SOC_DAILINK_REG(dsp), + }, + /* Back End DAI links */ { /* SSP0 - Codec */ diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index ac1dea5f9d11..5873abb46441 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -21,6 +21,7 @@ #include "../../codecs/da7219.h" #include "../../codecs/da7219-aad.h" #include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" #define BXT_DIALOG_CODEC_DAI "da7219-hifi" #define BXT_MAXIM_CODEC_DAI "HiFi" @@ -38,6 +39,7 @@ struct bxt_hdmi_pcm { struct bxt_card_private { struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; }; enum { @@ -615,6 +617,13 @@ static int bxt_card_late_probe(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, broxton_map, ARRAY_SIZE(broxton_map)); + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct bxt_hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -720,6 +729,8 @@ static int broxton_audio_probe(struct platform_device *pdev) if (ret) return ret; + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card); } diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index adf416a49b48..eabf9d8468ae 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -18,6 +18,7 @@ #include <sound/pcm_params.h> #include "../../codecs/hdac_hdmi.h" #include "../../codecs/rt298.h" +#include "hda_dsp_common.h" /* Headset jack detection DAPM pins */ static struct snd_soc_jack broxton_headset; @@ -31,6 +32,7 @@ struct bxt_hdmi_pcm { struct bxt_rt286_private { struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; }; enum { @@ -527,6 +529,13 @@ static int bxt_card_late_probe(struct snd_soc_card *card) int err, i = 0; char jack_name[NAME_SIZE]; + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct bxt_hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -626,6 +635,8 @@ static int broxton_audio_probe(struct platform_device *pdev) if (ret) return ret; + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + return devm_snd_soc_register_card(&pdev->dev, card); } diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 8879c3be29d5..c68a5b85a4a0 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -48,6 +48,7 @@ struct cht_mc_private { #define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */ #define CHT_RT5645_SSP0_AIF1 BIT(17) #define CHT_RT5645_SSP0_AIF2 BIT(18) +#define CHT_RT5645_PMC_PLT_CLK_0 BIT(19) static unsigned long cht_rt5645_quirk = 0; @@ -59,6 +60,8 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk SSP0_AIF1 enabled"); if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) dev_info(dev, "quirk SSP0_AIF2 enabled"); + if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0) + dev_info(dev, "quirk PMC_PLT_CLK_0 enabled"); } static int platform_clock_control(struct snd_soc_dapm_widget *w, @@ -226,16 +229,22 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } -/* uncomment when we have a real quirk static int cht_rt5645_quirk_cb(const struct dmi_system_id *id) { cht_rt5645_quirk = (unsigned long)id->driver_data; return 1; } -*/ static const struct dmi_system_id cht_rt5645_quirk_table[] = { { + /* Strago family Chromebooks */ + .callback = cht_rt5645_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"), + }, + .driver_data = (void *)CHT_RT5645_PMC_PLT_CLK_0, + }, + { }, }; @@ -526,6 +535,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int dai_index = 0; int ret_val = 0; int i; + const char *mclk_name; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) @@ -662,11 +672,15 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (ret_val) return ret_val; - drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0) + mclk_name = "pmc_plt_clk_0"; + else + mclk_name = "pmc_plt_clk_3"; + + drv->mclk = devm_clk_get(&pdev->dev, mclk_name); if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(drv->mclk)); + dev_err(&pdev->dev, "Failed to get MCLK from %s: %ld\n", + mclk_name, PTR_ERR(drv->mclk)); return PTR_ERR(drv->mclk); } diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c new file mode 100644 index 000000000000..a22f97234201 --- /dev/null +++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2019 Intel Corporation. + +/* + * Intel Cometlake I2S Machine driver for RT1011 + RT5682 codec + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/dmi.h> +#include <linux/slab.h> +#include <asm/cpu_device_id.h> +#include <linux/acpi.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/rt5682.h> +#include <sound/soc-acpi.h> +#include "../../codecs/rt1011.h" +#include "../../codecs/rt5682.h" +#include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" + +/* The platform clock outputs 24Mhz clock to codec as I2S MCLK */ +#define CML_PLAT_CLK 24000000 +#define CML_RT1011_CODEC_DAI "rt1011-aif" +#define CML_RT5682_CODEC_DAI "rt5682-aif1" +#define NAME_SIZE 32 + +static struct snd_soc_jack hdmi_jack[3]; + +struct hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct card_private { + char codec_name[SND_ACPI_I2C_ID_LEN]; + struct snd_soc_jack headset; + struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; +}; + +static const struct snd_kcontrol_new cml_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("TL Ext Spk"), + SOC_DAPM_PIN_SWITCH("TR Ext Spk"), + SOC_DAPM_PIN_SWITCH("WL Ext Spk"), + SOC_DAPM_PIN_SWITCH("WR Ext Spk"), +}; + +static const struct snd_soc_dapm_widget cml_rt1011_rt5682_widgets[] = { + SND_SOC_DAPM_SPK("TL Ext Spk", NULL), + SND_SOC_DAPM_SPK("TR Ext Spk", NULL), + SND_SOC_DAPM_SPK("WL Ext Spk", NULL), + SND_SOC_DAPM_SPK("WR Ext Spk", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route cml_rt1011_rt5682_map[] = { + /*speaker*/ + {"TL Ext Spk", NULL, "TL SPO"}, + {"TR Ext Spk", NULL, "TR SPO"}, + {"WL Ext Spk", NULL, "WL SPO"}, + {"WR Ext Spk", NULL, "WR SPO"}, + + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* other jacks */ + { "IN1P", NULL, "Headset Mic" }, + + /* DMIC */ + {"DMic", NULL, "SoC DMIC"}, +}; + +static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + /* need to enable ASRC function for 24MHz mclk rate */ + rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER | + RT5682_AD_STEREO1_FILTER, + RT5682_CLK_SEL_I2S1_ASRC); + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->headset; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + ret = snd_soc_component_set_jack(component, jack, NULL); + if (ret) + dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); + + return ret; +}; + +static int cml_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_dai *codec_dai = rtd->codec_dai; + int clk_id, clk_freq, pll_out, ret; + + clk_id = RT5682_PLL1_S_MCLK; + clk_freq = CML_PLAT_CLK; + + pll_out = params_rate(params) * 512; + + ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + if (ret < 0) + dev_warn(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret); + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + pll_out, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_warn(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + /* + * slot_width should be equal or large than data length, set them + * be the same + */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, + params_width(params)); + if (ret < 0) + dev_warn(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; +} + +static int cml_rt1011_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + struct snd_soc_card *card = rtd->card; + int srate, i, ret = 0; + + srate = params_rate(params); + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + + /* 100 Fs to drive 24 bit data */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK, + 100 * srate, 256 * srate); + if (ret < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, + RT1011_FS_SYS_PRE_S_PLL1, + 256 * srate, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return ret; + } + + /* + * Codec TDM is configured as 24 bit capture/ playback. + * 2 CH PB is done over 4 codecs - 2 Woofers and 2 Tweeters. + * The Left woofer and tweeter plays the Left playback data + * and similar by the Right. + * Hence 2 codecs (1 T and 1 W pair) share same Rx slot. + * The feedback is captured for each codec individually. + * Hence all 4 codecs use 1 Tx slot each for feedback. + */ + if (!strcmp(codec_dai->component->name, "i2c-10EC1011:00")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x4, 0x1, 4, 24); + if (ret < 0) + break; + } + if (!strcmp(codec_dai->component->name, "i2c-10EC1011:02")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x1, 0x1, 4, 24); + if (ret < 0) + break; + } + /* TDM Rx slot 2 is used for Right Woofer & Tweeters pair */ + if (!strcmp(codec_dai->component->name, "i2c-10EC1011:01")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x8, 0x2, 4, 24); + if (ret < 0) + break; + } + if (!strcmp(codec_dai->component->name, "i2c-10EC1011:03")) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + 0x2, 0x2, 4, 24); + if (ret < 0) + break; + } + } + if (ret < 0) + dev_err(rtd->dev, + "set codec TDM slot for %s failed with error %d\n", + codec_dai->component->name, ret); + return ret; +} + +static struct snd_soc_ops cml_rt5682_ops = { + .hw_params = cml_rt5682_hw_params, +}; + +static const struct snd_soc_ops cml_rt1011_ops = { + .hw_params = cml_rt1011_hw_params, +}; + +static int sof_card_late_probe(struct snd_soc_card *card) +{ + struct card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[NAME_SIZE]; + struct hdmi_pcm *pcm; + int ret, i = 0; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + ret = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &hdmi_jack[i], + NULL, 0); + if (ret) + return ret; + + ret = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &hdmi_jack[i]); + if (ret < 0) + return ret; + + i++; + } + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +static int hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct card_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +/* Cometlake digital audio interface glue - connects codec <--> CPU */ + +SND_SOC_DAILINK_DEF(ssp0_pin, + DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); +SND_SOC_DAILINK_DEF(ssp0_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", + CML_RT5682_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(ssp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("SSP1 Pin"))); +SND_SOC_DAILINK_DEF(ssp1_codec, + DAILINK_COMP_ARRAY( + /* WL */ COMP_CODEC("i2c-10EC1011:00", CML_RT1011_CODEC_DAI), + /* WR */ COMP_CODEC("i2c-10EC1011:01", CML_RT1011_CODEC_DAI), + /* TL */ COMP_CODEC("i2c-10EC1011:02", CML_RT1011_CODEC_DAI), + /* TR */ COMP_CODEC("i2c-10EC1011:03", CML_RT1011_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(dmic_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); + +SND_SOC_DAILINK_DEF(dmic16k_pin, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin"))); + +SND_SOC_DAILINK_DEF(dmic_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); + +SND_SOC_DAILINK_DEF(idisp1_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin"))); +SND_SOC_DAILINK_DEF(idisp1_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1"))); + +SND_SOC_DAILINK_DEF(idisp2_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin"))); +SND_SOC_DAILINK_DEF(idisp2_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2"))); + +SND_SOC_DAILINK_DEF(idisp3_pin, + DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin"))); +SND_SOC_DAILINK_DEF(idisp3_codec, + DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3"))); + +SND_SOC_DAILINK_DEF(platform, + DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3"))); + +static struct snd_soc_dai_link cml_rt1011_rt5682_dailink[] = { + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .init = cml_rt5682_codec_init, + .ignore_pmdown_time = 1, + .ops = &cml_rt5682_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(ssp0_pin, ssp0_codec, platform), + }, + { + .name = "dmic01", + .id = 1, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic_pin, dmic_codec, platform), + }, + { + .name = "dmic16k", + .id = 2, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(dmic16k_pin, dmic_codec, platform), + }, + { + .name = "iDisp1", + .id = 3, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp1_pin, idisp1_codec, platform), + }, + { + .name = "iDisp2", + .id = 4, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp2_pin, idisp2_codec, platform), + }, + { + .name = "iDisp3", + .id = 5, + .init = hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + SND_SOC_DAILINK_REG(idisp3_pin, idisp3_codec, platform), + }, + { + /* + * SSP1 - Codec : added to end of list ensuring + * reuse of common topologies for other end points + * and changing only SSP1's codec + */ + .name = "SSP1-Codec", + .id = 6, + .dpcm_playback = 1, + .dpcm_capture = 1, /* Capture stream provides Feedback */ + .no_pcm = 1, + .ops = &cml_rt1011_ops, + SND_SOC_DAILINK_REG(ssp1_pin, ssp1_codec, platform), + }, +}; + +static struct snd_soc_codec_conf rt1011_conf[] = { + { + .dev_name = "i2c-10EC1011:00", + .name_prefix = "WL", + }, + { + .dev_name = "i2c-10EC1011:01", + .name_prefix = "WR", + }, + { + .dev_name = "i2c-10EC1011:02", + .name_prefix = "TL", + }, + { + .dev_name = "i2c-10EC1011:03", + .name_prefix = "TR", + }, +}; + +/* Cometlake audio machine driver for RT1011 and RT5682 */ +static struct snd_soc_card snd_soc_card_cml = { + .name = "cml_rt1011_rt5682", + .dai_link = cml_rt1011_rt5682_dailink, + .num_links = ARRAY_SIZE(cml_rt1011_rt5682_dailink), + .codec_conf = rt1011_conf, + .num_configs = ARRAY_SIZE(rt1011_conf), + .dapm_widgets = cml_rt1011_rt5682_widgets, + .num_dapm_widgets = ARRAY_SIZE(cml_rt1011_rt5682_widgets), + .dapm_routes = cml_rt1011_rt5682_map, + .num_dapm_routes = ARRAY_SIZE(cml_rt1011_rt5682_map), + .controls = cml_controls, + .num_controls = ARRAY_SIZE(cml_controls), + .fully_routed = true, + .late_probe = sof_card_late_probe, +}; + +static int snd_cml_rt1011_probe(struct platform_device *pdev) +{ + struct card_private *ctx; + struct snd_soc_acpi_mach *mach; + const char *platform_name; + int ret; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + mach = (&pdev->dev)->platform_data; + snd_soc_card_cml.dev = &pdev->dev; + platform_name = mach->mach_params.platform; + + /* set platform name for each dailink */ + ret = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cml, + platform_name); + if (ret) + return ret; + + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + + snd_soc_card_set_drvdata(&snd_soc_card_cml, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cml); +} + +static struct platform_driver snd_cml_rt1011_rt5682_driver = { + .probe = snd_cml_rt1011_probe, + .driver = { + .name = "cml_rt1011_rt5682", + .pm = &snd_soc_pm_ops, + }, +}; +module_platform_driver(snd_cml_rt1011_rt5682_driver); + +/* Module information */ +MODULE_DESCRIPTION("Cometlake Audio Machine driver - RT1011 and RT5682 in I2S mode"); +MODULE_AUTHOR("Naveen Manohar <naveen.m@intel.com>"); +MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cml_rt1011_rt5682"); diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index bd2d371f2acd..b36264d1d1cd 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -19,6 +19,7 @@ #include <sound/soc-acpi.h> #include "../../codecs/rt5682.h" #include "../../codecs/hdac_hdmi.h" +#include "hda_dsp_common.h" /* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */ #define GLK_PLAT_CLK_FREQ 19200000 @@ -41,6 +42,7 @@ struct glk_hdmi_pcm { struct glk_card_private { struct snd_soc_jack geminilake_headset; struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; }; enum { @@ -545,6 +547,13 @@ static int glk_card_late_probe(struct snd_soc_card *card) int err = 0; int i = 0; + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct glk_hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -612,6 +621,8 @@ static int geminilake_audio_probe(struct platform_device *pdev) if (ret) return ret; + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + return devm_snd_soc_register_card(&pdev->dev, card); } diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c new file mode 100644 index 000000000000..ed36b68d6705 --- /dev/null +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2019 Intel Corporation. All rights reserved. + +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/hda_codec.h> +#include <sound/hda_i915.h> +#include "../../codecs/hdac_hda.h" + +#include "hda_dsp_common.h" + +/* + * Search card topology and return PCM device number + * matching Nth HDMI device (zero-based index). + */ +struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, + int hdmi_idx) +{ + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm *spcm; + int i = 0; + + for_each_card_rtds(card, rtd) { + spcm = rtd->pcm ? + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].pcm : NULL; + if (spcm && strstr(spcm->id, "HDMI")) { + if (i == hdmi_idx) + return rtd->pcm; + ++i; + } + } + + return NULL; +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +/* + * Search card topology and register HDMI PCM related controls + * to codec driver. + */ +int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, + struct snd_soc_component *comp) +{ + struct hdac_hda_priv *hda_pvt; + struct hda_codec *hcodec; + struct snd_pcm *spcm; + struct hda_pcm *hpcm; + int err = 0, i = 0; + + if (!comp) + return -EINVAL; + + hda_pvt = snd_soc_component_get_drvdata(comp); + hcodec = &hda_pvt->codec; + + list_for_each_entry(hpcm, &hcodec->pcm_list_head, list) { + spcm = hda_dsp_hdmi_pcm_handle(card, i); + if (spcm) { + hpcm->pcm = spcm; + hpcm->device = spcm->device; + dev_dbg(card->dev, + "%s: mapping HDMI converter %d to PCM %d (%p)\n", + __func__, i, hpcm->device, spcm); + } else { + hpcm->pcm = 0; + hpcm->device = SNDRV_PCM_INVALID_DEVICE; + dev_warn(card->dev, + "%s: no PCM in topology for HDMI converter %d\n\n", + __func__, i); + } + i++; + } + snd_hdac_display_power(hcodec->core.bus, + HDA_CODEC_IDX_CONTROLLER, true); + err = snd_hda_codec_build_controls(hcodec); + if (err < 0) + dev_err(card->dev, "unable to create controls %d\n", err); + snd_hdac_display_power(hcodec->core.bus, + HDA_CODEC_IDX_CONTROLLER, false); + + return err; +} + +#endif diff --git a/sound/soc/intel/boards/hda_dsp_common.h b/sound/soc/intel/boards/hda_dsp_common.h new file mode 100644 index 000000000000..431f7f09dccb --- /dev/null +++ b/sound/soc/intel/boards/hda_dsp_common.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2019 Intel Corporation. + */ + +/* + * This file defines helper functions used by multiple + * Intel HDA based machine drivers. + */ + +#ifndef __HDA_DSP_COMMON_H +#define __HDA_DSP_COMMON_H + +#include <sound/hda_codec.h> +#include <sound/hda_i915.h> +#include "../../codecs/hdac_hda.h" + +struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, + int hdmi_idx); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, + struct snd_soc_component *comp); +#else +static inline int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, + struct snd_soc_component *comp) +{ + return -EINVAL; +} +#endif + +#endif /* __HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 74dda8784f1a..3e5f6bead229 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -22,6 +22,9 @@ #include "../../codecs/rt5514.h" #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #define KBL_REALTEK_CODEC_DAI "rt5663-aif" #define KBL_REALTEK_DMIC_CODEC_DAI "rt5514-aif1" @@ -50,6 +53,8 @@ struct kbl_codec_private { struct snd_soc_jack kabylake_headset; struct list_head hdmi_pcm_list; struct snd_soc_jack kabylake_hdmi[2]; + struct clk *mclk; + struct clk *sclk; }; enum { @@ -71,6 +76,61 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("DMIC"), }; +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct kbl_codec_private *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + /* + * MCLK/SCLK need to be ON early for a successful synchronization of + * codec internal clock. And the clocks are turned off during + * POST_PMD after the stream is stopped. + */ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable MCLK */ + ret = clk_set_rate(priv->mclk, 24000000); + if (ret < 0) { + dev_err(card->dev, "Can't set rate for mclk, err: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, "Can't enable mclk, err: %d\n", ret); + return ret; + } + + /* Enable SCLK */ + ret = clk_set_rate(priv->sclk, 3072000); + if (ret < 0) { + dev_err(card->dev, "Can't set rate for sclk, err: %d\n", + ret); + clk_disable_unprepare(priv->mclk); + return ret; + } + + ret = clk_prepare_enable(priv->sclk); + if (ret < 0) { + dev_err(card->dev, "Can't enable sclk, err: %d\n", ret); + clk_disable_unprepare(priv->mclk); + } + break; + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(priv->mclk); + clk_disable_unprepare(priv->sclk); + break; + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -79,11 +139,15 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route kabylake_map[] = { /* Headphones */ + { "Headphone Jack", NULL, "Platform Clock" }, { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -92,6 +156,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "Right Spk", NULL, "Right BE_OUT" }, /* other jacks */ + { "Headset Mic", NULL, "Platform Clock" }, { "IN1P", NULL, "Headset Mic" }, { "IN1N", NULL, "Headset Mic" }, @@ -400,6 +465,9 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, dmic_constraints); + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } @@ -588,6 +656,55 @@ static struct snd_soc_dai_link kabylake_dais[] = { }, }; +static int kabylake_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct kbl_codec_private *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (!component || strcmp(component->name, RT5514_DEV_NAME)) + return 0; + + if (IS_ERR(priv->mclk)) + return 0; + + /* + * It's required to control mclk directly in the set_bias_level + * function for rt5514 codec or the recording function could + * break. + */ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_ON) { + dev_dbg(card->dev, "Disable mclk"); + clk_disable_unprepare(priv->mclk); + } else { + dev_dbg(card->dev, "Enable mclk"); + ret = clk_set_rate(priv->mclk, 24000000); + if (ret) { + dev_err(card->dev, "Can't set rate for mclk, err: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(card->dev, "Can't enable mclk, err: %d\n", + ret); + + /* mclk is already enabled in FW */ + ret = 0; + } + } + break; + default: + break; + } + + return ret; +} + static int kabylake_card_late_probe(struct snd_soc_card *card) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card); @@ -623,10 +740,11 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) * kabylake audio machine driver for MAX98927 + RT5514 + RT5663 */ static struct snd_soc_card kabylake_audio_card = { - .name = "kbl_r5514_5663_max", + .name = "kbl-r5514-5663-max", .owner = THIS_MODULE, .dai_link = kabylake_dais, .num_links = ARRAY_SIZE(kabylake_dais), + .set_bias_level = kabylake_set_bias_level, .controls = kabylake_controls, .num_controls = ARRAY_SIZE(kabylake_controls), .dapm_widgets = kabylake_widgets, @@ -643,6 +761,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; struct snd_soc_acpi_mach *mach; + int ret = 0; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -658,6 +777,34 @@ static int kabylake_audio_probe(struct platform_device *pdev) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; + ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk"); + if (IS_ERR(ctx->mclk)) { + ret = PTR_ERR(ctx->mclk); + if (ret == -ENOENT) { + dev_info(&pdev->dev, + "Failed to get ssp1_mclk, defer probe\n"); + return -EPROBE_DEFER; + } + + dev_err(&pdev->dev, "Failed to get ssp1_mclk with err:%d\n", + ret); + return ret; + } + + ctx->sclk = devm_clk_get(&pdev->dev, "ssp1_sclk"); + if (IS_ERR(ctx->sclk)) { + ret = PTR_ERR(ctx->sclk); + if (ret == -ENOENT) { + dev_info(&pdev->dev, + "Failed to get ssp1_sclk, defer probe\n"); + return -EPROBE_DEFER; + } + + dev_err(&pdev->dev, "Failed to get ssp1_sclk with err:%d\n", + ret); + return ret; + } + return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card); } diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index 58409b6e476e..eb419e1ec42b 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -14,6 +14,9 @@ #include "../../codecs/hdac_hdmi.h" #include "skl_hda_dsp_common.h" +#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h" + #define NAME_SIZE 32 int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) @@ -136,6 +139,9 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card) char jack_name[NAME_SIZE]; int err; + if (ctx->common_hdmi_codec_drv) + return skl_hda_hdmi_build_controls(card); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h index daa582e513b2..d6150670ca05 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.h +++ b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -8,12 +8,15 @@ * platforms with HDA Codecs. */ -#ifndef __SOUND_SOC_HDA_DSP_COMMON_H -#define __SOUND_SOC_HDA_DSP_COMMON_H +#ifndef __SKL_HDA_DSP_COMMON_H +#define __SKL_HDA_DSP_COMMON_H #include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> #include <sound/jack.h> +#include <sound/hda_codec.h> +#include "../../codecs/hdac_hda.h" +#include "hda_dsp_common.h" #define HDA_DSP_MAX_BE_DAI_LINKS 7 @@ -29,10 +32,30 @@ struct skl_hda_private { int pcm_count; int dai_index; const char *platform_name; + bool common_hdmi_codec_drv; }; extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; int skl_hda_hdmi_jack_init(struct snd_soc_card *card); int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device); +/* + * Search card topology and register HDMI PCM related controls + * to codec driver. + */ +static inline int skl_hda_hdmi_build_controls(struct snd_soc_card *card) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + struct skl_hda_hdmi_pcm *pcm; + + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct skl_hda_hdmi_pcm, + head); + component = pcm->codec_dai->component; + if (!component) + return -EINVAL; + + return hda_dsp_hdmi_build_controls(card, component); +} + #endif /* __SOUND_SOC_HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 1778acdc367c..4e45901e3a2f 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -90,7 +90,7 @@ skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) } static struct snd_soc_card hda_soc_card = { - .name = "skl_hda_card", + .name = "hda-dsp", .owner = THIS_MODULE, .dai_link = skl_hda_be_dai_links, .dapm_widgets = skl_hda_widgets, @@ -178,6 +178,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) ctx->pcm_count = hda_soc_card.num_links; ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ ctx->platform_name = mach->mach_params.platform; + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; hda_soc_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&hda_soc_card, ctx); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 4f6e58c3954a..751b8ea6ae1f 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -21,6 +21,7 @@ #include "../../codecs/rt5682.h" #include "../../codecs/hdac_hdmi.h" #include "../common/soc-intel-quirks.h" +#include "hda_dsp_common.h" #define NAME_SIZE 32 @@ -53,6 +54,7 @@ struct sof_card_private { struct clk *mclk; struct snd_soc_jack sof_headset; struct list_head hdmi_pcm_list; + bool common_hdmi_codec_drv; }; static int sof_rt5682_quirk_cb(const struct dmi_system_id *id) @@ -274,6 +276,13 @@ static int sof_card_late_probe(struct snd_soc_card *card) if (is_legacy_cpu) return 0; + pcm = list_first_entry(&ctx->hdmi_pcm_list, struct sof_hdmi_pcm, + head); + component = pcm->codec_dai->component; + + if (ctx->common_hdmi_codec_drv) + return hda_dsp_hdmi_build_controls(card, component); + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), @@ -370,7 +379,7 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) /* sof audio machine driver for rt5682 codec */ static struct snd_soc_card sof_audio_card_rt5682 = { - .name = "sof_rt5682", + .name = "rt5682", /* the sof- prefix is added by the core */ .owner = THIS_MODULE, .controls = sof_controls, .num_controls = ARRAY_SIZE(sof_controls), @@ -651,6 +660,8 @@ static int sof_audio_probe(struct platform_device *pdev) if (ret) return ret; + ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; + snd_soc_card_set_drvdata(&sof_audio_card_rt5682, ctx); return devm_snd_soc_register_card(&pdev->dev, diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 18d9630ae9a2..bd352878f89a 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,8 +7,10 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o \ + soc-acpi-intel-cnl-match.o soc-acpi-intel-cfl-match.o \ + soc-acpi-intel-cml-match.o soc-acpi-intel-icl-match.o \ soc-acpi-intel-tgl-match.o soc-acpi-intel-ehl-match.o \ + soc-acpi-intel-jsl-match.o \ soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o diff --git a/sound/soc/intel/common/soc-acpi-intel-cfl-match.c b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c new file mode 100644 index 000000000000..d6fd2026d0b8 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cfl-match.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-cfl-match.c - tables and support for CFL ACPI enumeration. + * + * Copyright (c) 2019, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cfl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c new file mode 100644 index 000000000000..5d08ae066738 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-acpi-intel-cml-match.c - tables and support for CML ACPI enumeration. + * + * Copyright (c) 2019, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static struct snd_soc_acpi_codecs cml_codecs = { + .num_codecs = 1, + .codecs = {"10EC5682"} +}; + +static struct snd_soc_acpi_codecs cml_spk_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { + { + .id = "DLGS7219", + .drv_name = "cml_da7219_max98357a", + .quirk_data = &cml_spk_codecs, + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg", + }, + { + .id = "MX98357A", + .drv_name = "sof_rt5682", + .quirk_data = &cml_codecs, + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg", + }, + { + .id = "10EC1011", + .drv_name = "cml_rt1011_rt5682", + .quirk_data = &cml_codecs, + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg", + }, + { + .id = "10EC5682", + .drv_name = "sof_rt5682", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt5682.tplg", + }, + + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 985aa366c9e8..27588841c8b0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -14,16 +14,6 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct snd_soc_acpi_codecs cml_codecs = { - .num_codecs = 1, - .codecs = {"10EC5682"} -}; - -static struct snd_soc_acpi_codecs cml_spk_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { { .id = "INT34C2", @@ -33,27 +23,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_fw_filename = "sof-cnl.ri", .sof_tplg_filename = "sof-cnl-rt274.tplg", }, - { - .id = "DLGS7219", - .drv_name = "cml_da7219_max98357a", - .quirk_data = &cml_spk_codecs, - .sof_fw_filename = "sof-cnl.ri", - .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg", - }, - { - .id = "MX98357A", - .drv_name = "sof_rt5682", - .quirk_data = &cml_codecs, - .sof_fw_filename = "sof-cnl.ri", - .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg", - }, - { - .id = "10EC5682", - .drv_name = "sof_rt5682", - .sof_fw_filename = "sof-cnl.ri", - .sof_tplg_filename = "sof-cml-rt5682.tplg", - }, - {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c new file mode 100644 index 000000000000..1c68a04f0c6e --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-jsl-match.c - tables and support for JSL ACPI enumeration. + * + * Copyright (c) 2019, Intel Corporation. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 7f4f6b755760..6e498a581d20 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -458,12 +458,12 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream, } /* this may get called several times by oss emulation */ -static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int hsw_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; @@ -656,16 +656,17 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int hsw_pcm_hw_free(struct snd_pcm_substream *substream) +static int hsw_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_lib_free_pages(substream); return 0; } -static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int hsw_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); struct hsw_pcm_data *pcm_data; struct sst_hsw_stream *sst_stream; @@ -770,11 +771,11 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) return pos; } -static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; @@ -795,10 +796,21 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) return offset; } -static int hsw_pcm_open(struct snd_pcm_substream *substream) +#ifdef CONFIG_SND_DMA_SGBUF +static struct page *hsw_pcm_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_sgbuf_ops_page(substream, offset); +} +#else +#define hsw_pcm_page NULL +#endif /* CONFIG_SND_DMA_SGBUF */ + +static int hsw_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; @@ -828,10 +840,10 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int hsw_pcm_close(struct snd_pcm_substream *substream) +static int hsw_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); struct hsw_pcm_data *pcm_data; struct sst_hsw *hsw = pdata->hsw; @@ -862,17 +874,6 @@ out: return ret; } -static const struct snd_pcm_ops hsw_pcm_ops = { - .open = hsw_pcm_open, - .close = hsw_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = hsw_pcm_hw_params, - .hw_free = hsw_pcm_hw_free, - .trigger = hsw_pcm_trigger, - .pointer = hsw_pcm_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) { struct sst_hsw *hsw = pdata->hsw; @@ -930,10 +931,10 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) } } -static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int hsw_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sst_pdata *pdata = dev_get_platdata(component->dev); struct hsw_priv_data *priv_data = dev_get_drvdata(component->dev); struct device *dev = pdata->dma_dev; @@ -1121,8 +1122,15 @@ static const struct snd_soc_component_driver hsw_dai_component = { .name = DRV_NAME, .probe = hsw_pcm_probe, .remove = hsw_pcm_remove, - .ops = &hsw_pcm_ops, - .pcm_new = hsw_pcm_new, + .open = hsw_pcm_open, + .close = hsw_pcm_close, + .hw_params = hsw_pcm_hw_params, + .hw_free = hsw_pcm_hw_free, + .trigger = hsw_pcm_trigger, + .pointer = hsw_pcm_pointer, + .page = hsw_pcm_page, + .ioctl = snd_soc_pcm_lib_ioctl, + .pcm_construct = hsw_pcm_new, .controls = hsw_volume_controls, .num_controls = ARRAY_SIZE(hsw_volume_controls), .dapm_widgets = widgets, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7f287424af9b..0850141c7d47 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1081,7 +1081,8 @@ int skl_dai_load(struct snd_soc_component *cmp, int index, return 0; } -static int skl_platform_open(struct snd_pcm_substream *substream) +static int skl_platform_soc_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai_link *dai_link = rtd->dai_link; @@ -1167,8 +1168,9 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, - int cmd) +static int skl_platform_soc_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int cmd) { struct hdac_bus *bus = get_bus_ctx(substream); @@ -1178,8 +1180,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static snd_pcm_uframes_t skl_platform_pcm_pointer - (struct snd_pcm_substream *substream) +static snd_pcm_uframes_t skl_platform_soc_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream); struct hdac_bus *bus = get_bus_ctx(substream); @@ -1225,6 +1228,24 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer return bytes_to_frames(substream->runtime, pos); } +static int skl_platform_soc_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + return snd_pcm_lib_default_mmap(substream, area); +} + +#ifdef CONFIG_SND_DMA_SGBUF +static struct page *skl_platform_soc_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_sgbuf_ops_page(substream, offset); +} +#else +#define skl_platform_soc_page NULL +#endif /* CONFIG_SND_DMA_SGBUF */ + static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec) { @@ -1245,7 +1266,9 @@ static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream, return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; } -static int skl_get_time_info(struct snd_pcm_substream *substream, +static int skl_platform_soc_get_time_info( + struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) @@ -1277,24 +1300,16 @@ static int skl_get_time_info(struct snd_pcm_substream *substream, return 0; } -static const struct snd_pcm_ops skl_platform_ops = { - .open = skl_platform_open, - .ioctl = snd_pcm_lib_ioctl, - .trigger = skl_platform_pcm_trigger, - .pointer = skl_platform_pcm_pointer, - .get_time_info = skl_get_time_info, - .mmap = snd_pcm_lib_default_mmap, - .page = snd_pcm_sgbuf_ops_page, -}; - -static void skl_pcm_free(struct snd_pcm *pcm) +static void skl_platform_soc_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); } #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) -static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int skl_platform_soc_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; struct hdac_bus *bus = dev_get_drvdata(dai->dev); @@ -1458,7 +1473,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return 0; } -static void skl_pcm_remove(struct snd_soc_component *component) +static void skl_platform_soc_remove(struct snd_soc_component *component) { struct hdac_bus *bus = dev_get_drvdata(component->dev); struct skl_dev *skl = bus_to_skl(bus); @@ -1471,10 +1486,16 @@ static void skl_pcm_remove(struct snd_soc_component *component) static const struct snd_soc_component_driver skl_component = { .name = "pcm", .probe = skl_platform_soc_probe, - .remove = skl_pcm_remove, - .ops = &skl_platform_ops, - .pcm_new = skl_pcm_new, - .pcm_free = skl_pcm_free, + .remove = skl_platform_soc_remove, + .open = skl_platform_soc_open, + .ioctl = snd_soc_pcm_lib_ioctl, + .trigger = skl_platform_soc_trigger, + .pointer = skl_platform_soc_pointer, + .get_time_info = skl_platform_soc_get_time_info, + .mmap = skl_platform_soc_mmap, + .page = skl_platform_soc_page, + .pcm_construct = skl_platform_soc_new, + .pcm_destruct = skl_platform_soc_free, .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ }; diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 13408de34055..38d48d101783 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -497,15 +497,13 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) struct jz4740_i2s *i2s; struct resource *mem; int ret; - const struct of_device_id *match; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) return -ENOMEM; - match = of_match_device(jz4740_of_matches, &pdev->dev); - if (match) - i2s->version = (enum jz47xx_i2s_version)match->data; + i2s->version = + (enum jz47xx_i2s_version)of_device_get_match_data(&pdev->dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2s->base = devm_ioremap_resource(&pdev->dev, mem); diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 6f69f314f2c2..e28fb3449f1d 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -98,7 +98,8 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win, } } -static int kirkwood_dma_open(struct snd_pcm_substream *substream) +static int kirkwood_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { int err; struct snd_pcm_runtime *runtime = substream->runtime; @@ -132,7 +133,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED, "kirkwood-i2s", priv); if (err) - return -EBUSY; + return err; /* * Enable Error interrupts. We're only ack'ing them but @@ -160,7 +161,8 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) return 0; } -static int kirkwood_dma_close(struct snd_pcm_substream *substream) +static int kirkwood_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct kirkwood_dma_data *priv = kirkwood_priv(substream); @@ -180,8 +182,9 @@ static int kirkwood_dma_close(struct snd_pcm_substream *substream) return 0; } -static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int kirkwood_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -191,13 +194,15 @@ static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream, return 0; } -static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream) +static int kirkwood_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); return 0; } -static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) +static int kirkwood_dma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct kirkwood_dma_data *priv = kirkwood_priv(substream); @@ -222,8 +227,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) return 0; } -static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream - *substream) +static snd_pcm_uframes_t kirkwood_dma_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct kirkwood_dma_data *priv = kirkwood_priv(substream); snd_pcm_uframes_t count; @@ -238,16 +244,6 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream return count; } -static const struct snd_pcm_ops kirkwood_dma_ops = { - .open = kirkwood_dma_open, - .close = kirkwood_dma_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = kirkwood_dma_hw_params, - .hw_free = kirkwood_dma_hw_free, - .prepare = kirkwood_dma_prepare, - .pointer = kirkwood_dma_pointer, -}; - static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { @@ -267,7 +263,8 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm, return 0; } -static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd) +static int kirkwood_dma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -294,7 +291,8 @@ static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) +static void kirkwood_dma_free_dma_buffers(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -316,7 +314,13 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) const struct snd_soc_component_driver kirkwood_soc_component = { .name = DRV_NAME, - .ops = &kirkwood_dma_ops, - .pcm_new = kirkwood_dma_new, - .pcm_free = kirkwood_dma_free_dma_buffers, + .open = kirkwood_dma_open, + .close = kirkwood_dma_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = kirkwood_dma_hw_params, + .hw_free = kirkwood_dma_hw_free, + .prepare = kirkwood_dma_prepare, + .pointer = kirkwood_dma_pointer, + .pcm_construct = kirkwood_dma_new, + .pcm_destruct = kirkwood_dma_free_dma_buffers, }; diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 111e44b64b38..a656d2014127 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -125,6 +125,7 @@ config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A select SND_SOC_MAX98357A select SND_SOC_BT_SCO select SND_SOC_TS3A227E + select SND_SOC_CROS_EC_CODEC if CROS_EC help This adds ASoC driver for Mediatek MT8183 boards with the MT6358 TS3A227E MAX98357A audio codec. diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 3ce527ce30ce..b6624d8d084b 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -77,11 +77,10 @@ int mtk_afe_add_sub_dai_control(struct snd_soc_component *component) } EXPORT_SYMBOL_GPL(mtk_afe_add_sub_dai_control); -static snd_pcm_uframes_t mtk_afe_pcm_pointer - (struct snd_pcm_substream *substream) +snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; const struct mtk_base_memif_data *memif_data = memif->data; @@ -111,18 +110,13 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer POINTER_RETURN_FRAMES: return bytes_to_frames(substream->runtime, pcm_ptr_bytes); } +EXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer); -const struct snd_pcm_ops mtk_afe_pcm_ops = { - .ioctl = snd_pcm_lib_ioctl, - .pointer = mtk_afe_pcm_pointer, -}; -EXPORT_SYMBOL_GPL(mtk_afe_pcm_ops); - -int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +int mtk_afe_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { size_t size; struct snd_pcm *pcm = rtd->pcm; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); size = afe->mtk_afe_hardware->buffer_bytes_max; @@ -132,17 +126,19 @@ int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) } EXPORT_SYMBOL_GPL(mtk_afe_pcm_new); -void mtk_afe_pcm_free(struct snd_pcm *pcm) +void mtk_afe_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); } EXPORT_SYMBOL_GPL(mtk_afe_pcm_free); const struct snd_soc_component_driver mtk_afe_pcm_platform = { - .name = AFE_PCM_NAME, - .ops = &mtk_afe_pcm_ops, - .pcm_new = mtk_afe_pcm_new, - .pcm_free = mtk_afe_pcm_free, + .name = AFE_PCM_NAME, + .ioctl = snd_soc_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, + .pcm_destruct = mtk_afe_pcm_free, }; EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform); diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h index 88df6797732f..e550d11568c3 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h @@ -10,7 +10,6 @@ #define _MTK_AFE_PLATFORM_DRIVER_H_ #define AFE_PCM_NAME "mtk-afe-pcm" -extern const struct snd_pcm_ops mtk_afe_pcm_ops; extern const struct snd_soc_component_driver mtk_afe_pcm_platform; struct mtk_base_afe; @@ -18,9 +17,12 @@ struct snd_pcm; struct snd_soc_component; struct snd_soc_pcm_runtime; - -int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd); -void mtk_afe_pcm_free(struct snd_pcm *pcm); +snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream); +int mtk_afe_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd); +void mtk_afe_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm); int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe); int mtk_afe_add_sub_dai_control(struct snd_soc_component *component); diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c index d00608c73c6e..2b490ae2e642 100644 --- a/sound/soc/mediatek/common/mtk-btcvsd.c +++ b/sound/soc/mediatek/common/mtk-btcvsd.c @@ -875,11 +875,9 @@ static const struct snd_pcm_hardware mtk_btcvsd_hardware = { .fifo_size = 0, }; -static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream) +static int mtk_pcm_btcvsd_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); int ret; @@ -899,11 +897,9 @@ static int mtk_pcm_btcvsd_open(struct snd_pcm_substream *substream) return ret; } -static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream) +static int mtk_pcm_btcvsd_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); @@ -914,12 +910,10 @@ static int mtk_pcm_btcvsd_close(struct snd_pcm_substream *substream) return 0; } -static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream, +static int mtk_pcm_btcvsd_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && @@ -934,11 +928,9 @@ static int mtk_pcm_btcvsd_hw_params(struct snd_pcm_substream *substream, return 0; } -static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream) +static int mtk_pcm_btcvsd_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -947,11 +939,9 @@ static int mtk_pcm_btcvsd_hw_free(struct snd_pcm_substream *substream) return 0; } -static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream) +static int mtk_pcm_btcvsd_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); @@ -961,11 +951,9 @@ static int mtk_pcm_btcvsd_prepare(struct snd_pcm_substream *substream) return 0; } -static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd) +static int mtk_pcm_btcvsd_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *bt_stream = get_bt_stream(bt, substream); int stream = substream->stream; @@ -993,12 +981,10 @@ static int mtk_pcm_btcvsd_trigger(struct snd_pcm_substream *substream, int cmd) } } -static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer - (struct snd_pcm_substream *substream) +static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); struct mtk_btcvsd_snd_stream *bt_stream; snd_pcm_uframes_t frame = 0; @@ -1044,13 +1030,11 @@ static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer return frame; } -static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream, +static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channel, unsigned long pos, void __user *buf, unsigned long count) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, BTCVSD_SND_NAME); struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -1061,18 +1045,6 @@ static int mtk_pcm_btcvsd_copy(struct snd_pcm_substream *substream, return 0; } -static struct snd_pcm_ops mtk_btcvsd_ops = { - .open = mtk_pcm_btcvsd_open, - .close = mtk_pcm_btcvsd_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = mtk_pcm_btcvsd_hw_params, - .hw_free = mtk_pcm_btcvsd_hw_free, - .prepare = mtk_pcm_btcvsd_prepare, - .trigger = mtk_pcm_btcvsd_trigger, - .pointer = mtk_pcm_btcvsd_pointer, - .copy_user = mtk_pcm_btcvsd_copy, -}; - /* kcontrol */ static const char *const btsco_band_str[] = {"NB", "WB"}; @@ -1295,9 +1267,17 @@ static int mtk_btcvsd_snd_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { - .name = BTCVSD_SND_NAME, - .ops = &mtk_btcvsd_ops, - .probe = mtk_btcvsd_snd_component_probe, + .name = BTCVSD_SND_NAME, + .probe = mtk_btcvsd_snd_component_probe, + .open = mtk_pcm_btcvsd_open, + .close = mtk_pcm_btcvsd_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = mtk_pcm_btcvsd_hw_params, + .hw_free = mtk_pcm_btcvsd_hw_free, + .prepare = mtk_pcm_btcvsd_prepare, + .trigger = mtk_pcm_btcvsd_trigger, + .pointer = mtk_pcm_btcvsd_pointer, + .copy_user = mtk_pcm_btcvsd_copy, }; static int mtk_btcvsd_snd_probe(struct platform_device *pdev) diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index e52c032d53aa..033c07fb599c 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -710,11 +710,12 @@ static int mt6797_afe_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver mt6797_afe_component = { - .name = AFE_PCM_NAME, - .ops = &mtk_afe_pcm_ops, - .pcm_new = mtk_afe_pcm_new, - .pcm_free = mtk_afe_pcm_free, - .probe = mt6797_afe_component_probe, + .name = AFE_PCM_NAME, + .probe = mt6797_afe_component_probe, + .ioctl = snd_soc_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, + .pcm_destruct = mtk_afe_pcm_free, }; static int mt6797_dai_memif_register(struct mtk_base_afe *afe) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 4a31106d3471..76af09d8f1af 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -11,6 +11,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include "mt8183-afe-common.h" #include "mt8183-afe-clk.h" @@ -1047,11 +1048,12 @@ static int mt8183_afe_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver mt8183_afe_component = { - .name = AFE_PCM_NAME, - .ops = &mtk_afe_pcm_ops, - .pcm_new = mtk_afe_pcm_new, - .pcm_free = mtk_afe_pcm_free, - .probe = mt8183_afe_component_probe, + .name = AFE_PCM_NAME, + .probe = mt8183_afe_component_probe, + .ioctl = snd_soc_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, + .pcm_destruct = mtk_afe_pcm_free, }; static int mt8183_dai_memif_register(struct mtk_base_afe *afe) @@ -1089,6 +1091,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_base_afe *afe; struct mt8183_afe_private *afe_priv; struct device *dev; + struct reset_control *rstc; int i, irq_id, ret; afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); @@ -1126,6 +1129,19 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) return ret; } + rstc = devm_reset_control_get(dev, "audiosys"); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + dev_err(dev, "could not get audiosys reset:%d\n", ret); + return ret; + } + + ret = reset_control_reset(rstc); + if (ret) { + dev_err(dev, "failed to trigger audio reset:%d\n", ret); + return ret; + } + /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; pm_runtime_get_sync(&pdev->dev); diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index bb9cdc0d6552..0555f7d73d05 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -19,11 +19,12 @@ enum PINCTRL_PIN_STATE { PIN_STATE_DEFAULT = 0, PIN_TDM_OUT_ON, PIN_TDM_OUT_OFF, + PIN_WOV, PIN_STATE_MAX }; static const char * const mt8183_pin_str[PIN_STATE_MAX] = { - "default", "aud_tdm_out_on", "aud_tdm_out_off", + "default", "aud_tdm_out_on", "aud_tdm_out_off", "wov", }; struct mt8183_mt6358_ts3a227_max98357_priv { @@ -142,6 +143,11 @@ SND_SOC_DAILINK_DEFS(playback_hdmi, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(wake_on_voice, + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + /* BE */ SND_SOC_DAILINK_DEFS(primary_codec, DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), @@ -229,6 +235,41 @@ static struct snd_soc_ops mt8183_mt6358_tdm_ops = { .shutdown = mt8183_mt6358_tdm_shutdown, }; +static int +mt8183_mt6358_ts3a227_max98357_wov_startup( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct mt8183_mt6358_ts3a227_max98357_priv *priv = + snd_soc_card_get_drvdata(card); + + return pinctrl_select_state(priv->pinctrl, + priv->pin_states[PIN_WOV]); +} + +static void +mt8183_mt6358_ts3a227_max98357_wov_shutdown( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct mt8183_mt6358_ts3a227_max98357_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + ret = pinctrl_select_state(priv->pinctrl, + priv->pin_states[PIN_STATE_DEFAULT]); + if (ret) + dev_err(card->dev, "%s failed to select state %d\n", + __func__, ret); +} + +static const struct snd_soc_ops mt8183_mt6358_ts3a227_max98357_wov_ops = { + .startup = mt8183_mt6358_ts3a227_max98357_wov_startup, + .shutdown = mt8183_mt6358_ts3a227_max98357_wov_shutdown, +}; + static struct snd_soc_dai_link mt8183_mt6358_ts3a227_max98357_dai_links[] = { /* FE */ @@ -306,6 +347,15 @@ mt8183_mt6358_ts3a227_max98357_dai_links[] = { .dpcm_playback = 1, SND_SOC_DAILINK_REG(playback_hdmi), }, + { + .name = "Wake on Voice", + .stream_name = "Wake on Voice", + .ignore_suspend = 1, + .ignore = 1, + SND_SOC_DAILINK_REG(wake_on_voice), + .ops = &mt8183_mt6358_ts3a227_max98357_wov_ops, + }, + /* BE */ { .name = "Primary Codec", @@ -429,7 +479,7 @@ static int mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8183_mt6358_ts3a227_max98357_card; - struct device_node *platform_node; + struct device_node *platform_node, *ec_codec; struct snd_soc_dai_link *dai_link; struct mt8183_mt6358_ts3a227_max98357_priv *priv; int ret; @@ -444,10 +494,24 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } + ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); + for_each_card_prelinks(card, i, dai_link) { if (dai_link->platforms->name) continue; - dai_link->platforms->of_node = platform_node; + + if (ec_codec && strcmp(dai_link->name, "Wake on Voice") == 0) { + dai_link->cpus[0].name = NULL; + dai_link->cpus[0].of_node = ec_codec; + dai_link->cpus[0].dai_name = NULL; + dai_link->codecs[0].name = NULL; + dai_link->codecs[0].of_node = ec_codec; + dai_link->codecs[0].dai_name = "Wake on Voice"; + dai_link->platforms[0].of_node = ec_codec; + dai_link->ignore = 0; + } else { + dai_link->platforms->of_node = platform_node; + } } mt8183_mt6358_ts3a227_max98357_headset_dev.dlc.of_node = diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index 5a3749938900..d6f3eefb8f09 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -70,7 +70,8 @@ static void __dma_enable(struct axg_fifo *fifo, bool enable) enable ? CTRL0_DMA_EN : 0); } -static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +int axg_fifo_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *ss, int cmd) { struct axg_fifo *fifo = axg_fifo_data(ss); @@ -91,8 +92,10 @@ static int axg_fifo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) return 0; } +EXPORT_SYMBOL_GPL(axg_fifo_pcm_trigger); -static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss) +snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct axg_fifo *fifo = axg_fifo_data(ss); struct snd_pcm_runtime *runtime = ss->runtime; @@ -102,9 +105,11 @@ static snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_pcm_substream *ss) return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); } +EXPORT_SYMBOL_GPL(axg_fifo_pcm_pointer); -static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *params) +int axg_fifo_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = ss->runtime; struct axg_fifo *fifo = axg_fifo_data(ss); @@ -132,15 +137,17 @@ static int axg_fifo_pcm_hw_params(struct snd_pcm_substream *ss, return 0; } +EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_params); -static int g12a_fifo_pcm_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *params) +int g12a_fifo_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params) { struct axg_fifo *fifo = axg_fifo_data(ss); struct snd_pcm_runtime *runtime = ss->runtime; int ret; - ret = axg_fifo_pcm_hw_params(ss, params); + ret = axg_fifo_pcm_hw_params(component, ss, params); if (ret) return ret; @@ -149,8 +156,10 @@ static int g12a_fifo_pcm_hw_params(struct snd_pcm_substream *ss, return 0; } +EXPORT_SYMBOL_GPL(g12a_fifo_pcm_hw_params); -static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss) +int axg_fifo_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct axg_fifo *fifo = axg_fifo_data(ss); @@ -160,6 +169,7 @@ static int axg_fifo_pcm_hw_free(struct snd_pcm_substream *ss) return snd_pcm_lib_free_pages(ss); } +EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free); static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) { @@ -194,7 +204,8 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) return IRQ_RETVAL(status); } -static int axg_fifo_pcm_open(struct snd_pcm_substream *ss) +int axg_fifo_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct axg_fifo *fifo = axg_fifo_data(ss); struct device *dev = axg_fifo_dev(ss); @@ -250,8 +261,10 @@ static int axg_fifo_pcm_open(struct snd_pcm_substream *ss) return ret; } +EXPORT_SYMBOL_GPL(axg_fifo_pcm_open); -static int axg_fifo_pcm_close(struct snd_pcm_substream *ss) +int axg_fifo_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct axg_fifo *fifo = axg_fifo_data(ss); int ret; @@ -267,28 +280,7 @@ static int axg_fifo_pcm_close(struct snd_pcm_substream *ss) return ret; } - -const struct snd_pcm_ops axg_fifo_pcm_ops = { - .open = axg_fifo_pcm_open, - .close = axg_fifo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = axg_fifo_pcm_hw_params, - .hw_free = axg_fifo_pcm_hw_free, - .pointer = axg_fifo_pcm_pointer, - .trigger = axg_fifo_pcm_trigger, -}; -EXPORT_SYMBOL_GPL(axg_fifo_pcm_ops); - -const struct snd_pcm_ops g12a_fifo_pcm_ops = { - .open = axg_fifo_pcm_open, - .close = axg_fifo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = g12a_fifo_pcm_hw_params, - .hw_free = axg_fifo_pcm_hw_free, - .pointer = axg_fifo_pcm_pointer, - .trigger = axg_fifo_pcm_trigger, -}; -EXPORT_SYMBOL_GPL(g12a_fifo_pcm_ops); +EXPORT_SYMBOL_GPL(axg_fifo_pcm_close); int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type) { diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index bb1e2ce50256..cf928d43b558 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -15,7 +15,7 @@ struct reset_control; struct snd_soc_component_driver; struct snd_soc_dai; struct snd_soc_dai_driver; -struct snd_pcm_ops; + struct snd_soc_pcm_runtime; #define AXG_FIFO_CH_MAX 128 @@ -75,8 +75,22 @@ struct axg_fifo_match_data { struct snd_soc_dai_driver *dai_drv; }; -extern const struct snd_pcm_ops axg_fifo_pcm_ops; -extern const struct snd_pcm_ops g12a_fifo_pcm_ops; +int axg_fifo_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *ss); +int axg_fifo_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *ss); +int axg_fifo_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params); +int g12a_fifo_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *params); +int axg_fifo_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *ss); +snd_pcm_uframes_t axg_fifo_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *ss); +int axg_fifo_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *ss, int cmd); int axg_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, unsigned int type); int axg_fifo_probe(struct platform_device *pdev); diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 6ab111c31b28..665d75d49d7b 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -149,7 +149,13 @@ static const struct snd_soc_component_driver axg_frddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(axg_frddr_dapm_widgets), .dapm_routes = axg_frddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(axg_frddr_dapm_routes), - .ops = &axg_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = axg_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data axg_frddr_match_data = { @@ -267,7 +273,13 @@ static const struct snd_soc_component_driver g12a_frddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(g12a_frddr_dapm_widgets), .dapm_routes = g12a_frddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), - .ops = &g12a_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = g12a_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data g12a_frddr_match_data = { @@ -331,7 +343,13 @@ static const struct snd_soc_component_driver sm1_frddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(sm1_frddr_dapm_widgets), .dapm_routes = g12a_frddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(g12a_frddr_dapm_routes), - .ops = &g12a_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = g12a_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data sm1_frddr_match_data = { diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index c8ea2145f576..7fef0b961496 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -181,7 +181,13 @@ static const struct snd_soc_component_driver axg_toddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), .dapm_routes = axg_toddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), - .ops = &axg_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = axg_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data axg_toddr_match_data = { @@ -214,7 +220,13 @@ static const struct snd_soc_component_driver g12a_toddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), .dapm_routes = axg_toddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), - .ops = &g12a_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = g12a_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data g12a_toddr_match_data = { @@ -278,7 +290,13 @@ static const struct snd_soc_component_driver sm1_toddr_component_drv = { .num_dapm_widgets = ARRAY_SIZE(sm1_toddr_dapm_widgets), .dapm_routes = sm1_toddr_dapm_routes, .num_dapm_routes = ARRAY_SIZE(sm1_toddr_dapm_routes), - .ops = &g12a_fifo_pcm_ops + .open = axg_fifo_pcm_open, + .close = axg_fifo_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = g12a_fifo_pcm_hw_params, + .hw_free = axg_fifo_pcm_hw_free, + .pointer = axg_fifo_pcm_pointer, + .trigger = axg_fifo_pcm_trigger, }; static const struct axg_fifo_match_data sm1_toddr_match_data = { diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 7096b5263e25..54a4c9213e83 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -55,8 +55,9 @@ static struct snd_pcm_hardware mmp_pcm_hardware[] = { }, }; -static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int mmp_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct dma_slave_config slave_config; @@ -77,6 +78,18 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } +static int mmp_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + return snd_dmaengine_pcm_trigger(substream, cmd); +} + +static snd_pcm_uframes_t mmp_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_pointer(substream); +} + static bool filter(struct dma_chan *chan, void *param) { struct mmp_dma_data *dma_data = param; @@ -94,10 +107,10 @@ static bool filter(struct dma_chan *chan, void *param) return found; } -static int mmp_pcm_open(struct snd_pcm_substream *substream) +static int mmp_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct platform_device *pdev = to_platform_device(component->dev); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct mmp_dma_data dma_data; @@ -117,8 +130,15 @@ static int mmp_pcm_open(struct snd_pcm_substream *substream) &dma_data); } -static int mmp_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int mmp_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_close_release_chan(substream); +} + +static int mmp_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; unsigned long off = vma->vm_pgoff; @@ -129,17 +149,8 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream, vma->vm_end - vma->vm_start, vma->vm_page_prot); } -static const struct snd_pcm_ops mmp_pcm_ops = { - .open = mmp_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = mmp_pcm_hw_params, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, - .mmap = mmp_pcm_mmap, -}; - -static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm) +static void mmp_pcm_free_dma_buffers(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -188,7 +199,8 @@ static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, return 0; } -static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int mmp_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm_substream *substream; struct snd_pcm *pcm = rtd->pcm; @@ -205,15 +217,21 @@ static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; err: - mmp_pcm_free_dma_buffers(pcm); + mmp_pcm_free_dma_buffers(component, pcm); return ret; } static const struct snd_soc_component_driver mmp_soc_component = { .name = DRV_NAME, - .ops = &mmp_pcm_ops, - .pcm_new = mmp_pcm_new, - .pcm_free = mmp_pcm_free_dma_buffers, + .open = mmp_pcm_open, + .close = mmp_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = mmp_pcm_hw_params, + .trigger = mmp_pcm_trigger, + .pointer = mmp_pcm_pointer, + .mmap = mmp_pcm_mmap, + .pcm_construct = mmp_pcm_new, + .pcm_destruct = mmp_pcm_free_dma_buffers, }; static int mmp_pcm_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 48d5c2252b10..59ef04d0467a 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -56,7 +56,7 @@ static void poodle_ext_control(struct snd_soc_dapm_context *dapm) snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); } - /* set the enpoints to their new connetion states */ + /* set the endpoints to their new connection states */ if (poodle_spk_func == POODLE_SPK_ON) snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 5fdd1a24c232..49cb3ba1519f 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -869,9 +869,17 @@ static struct snd_soc_dai_driver pxa_ssp_dai = { static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", - .ops = &pxa2xx_pcm_ops, - .pcm_new = pxa2xx_soc_pcm_new, - .pcm_free = pxa2xx_pcm_free_dma_buffers, + .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_destruct = pxa2xx_soc_pcm_free, + .open = pxa2xx_soc_pcm_open, + .close = pxa2xx_soc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = pxa2xx_soc_pcm_hw_params, + .hw_free = pxa2xx_soc_pcm_hw_free, + .prepare = pxa2xx_soc_pcm_prepare, + .trigger = pxa2xx_soc_pcm_trigger, + .pointer = pxa2xx_soc_pcm_pointer, + .mmap = pxa2xx_soc_pcm_mmap, }; #ifdef CONFIG_OF diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index bf28187315db..31e81a6f616f 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -204,9 +204,17 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { static const struct snd_soc_component_driver pxa_ac97_component = { .name = "pxa-ac97", - .ops = &pxa2xx_pcm_ops, - .pcm_new = pxa2xx_soc_pcm_new, - .pcm_free = pxa2xx_pcm_free_dma_buffers, + .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_destruct = pxa2xx_soc_pcm_free, + .open = pxa2xx_soc_pcm_open, + .close = pxa2xx_soc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = pxa2xx_soc_pcm_hw_params, + .hw_free = pxa2xx_soc_pcm_hw_free, + .prepare = pxa2xx_soc_pcm_prepare, + .trigger = pxa2xx_soc_pcm_trigger, + .pointer = pxa2xx_soc_pcm_pointer, + .mmap = pxa2xx_soc_pcm_mmap, }; #ifdef CONFIG_OF diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 9f7fb7335ac0..e77d707efde7 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -360,9 +360,17 @@ static struct snd_soc_dai_driver pxa_i2s_dai = { static const struct snd_soc_component_driver pxa_i2s_component = { .name = "pxa-i2s", - .ops = &pxa2xx_pcm_ops, - .pcm_new = pxa2xx_soc_pcm_new, - .pcm_free = pxa2xx_pcm_free_dma_buffers, + .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_destruct = pxa2xx_soc_pcm_free, + .open = pxa2xx_soc_pcm_open, + .close = pxa2xx_soc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = pxa2xx_soc_pcm_hw_params, + .hw_free = pxa2xx_soc_pcm_hw_free, + .prepare = pxa2xx_soc_pcm_prepare, + .trigger = pxa2xx_soc_pcm_trigger, + .pointer = pxa2xx_soc_pcm_pointer, + .mmap = pxa2xx_soc_pcm_mmap, }; static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 74b56fa0870f..07b3455a6f23 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -18,9 +18,17 @@ #include <sound/dmaengine_pcm.h> static const struct snd_soc_component_driver pxa2xx_soc_platform = { - .ops = &pxa2xx_pcm_ops, - .pcm_new = pxa2xx_soc_pcm_new, - .pcm_free = pxa2xx_pcm_free_dma_buffers, + .pcm_construct = pxa2xx_soc_pcm_new, + .pcm_destruct = pxa2xx_soc_pcm_free, + .open = pxa2xx_soc_pcm_open, + .close = pxa2xx_soc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = pxa2xx_soc_pcm_hw_params, + .hw_free = pxa2xx_soc_pcm_hw_free, + .prepare = pxa2xx_soc_pcm_prepare, + .trigger = pxa2xx_soc_pcm_trigger, + .pointer = pxa2xx_soc_pcm_pointer, + .mmap = pxa2xx_soc_pcm_mmap, }; static int pxa2xx_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 4c745baa39f7..2e8892316423 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -50,12 +50,12 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { .fifo_size = 0, }; -static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; int ret, dma_ch, dir = substream->stream; @@ -105,11 +105,10 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) return 0; } -static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; struct lpass_pcm_data *data; @@ -122,11 +121,11 @@ static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) return 0; } -static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -216,10 +215,10 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, return 0; } -static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -236,11 +235,11 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) return ret; } -static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -288,11 +287,11 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) return 0; } -static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, - int cmd) +static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int cmd) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -363,10 +362,10 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } static snd_pcm_uframes_t lpass_platform_pcmops_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -395,8 +394,9 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( return bytes_to_frames(substream->runtime, curr_addr - base_addr); } -static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int lpass_platform_pcmops_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -405,18 +405,6 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); } -static const struct snd_pcm_ops lpass_platform_pcm_ops = { - .open = lpass_platform_pcmops_open, - .close = lpass_platform_pcmops_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = lpass_platform_pcmops_hw_params, - .hw_free = lpass_platform_pcmops_hw_free, - .prepare = lpass_platform_pcmops_prepare, - .trigger = lpass_platform_pcmops_trigger, - .pointer = lpass_platform_pcmops_pointer, - .mmap = lpass_platform_pcmops_mmap, -}; - static irqreturn_t lpass_dma_interrupt_handler( struct snd_pcm_substream *substream, struct lpass_data *drvdata, @@ -499,11 +487,11 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } -static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) +static int lpass_platform_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm_substream *psubstream, *csubstream; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); int ret = -EINVAL; size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; @@ -535,7 +523,8 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) return 0; } -static void lpass_platform_pcm_free(struct snd_pcm *pcm) +static void lpass_platform_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; int i; @@ -552,9 +541,18 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm) static const struct snd_soc_component_driver lpass_component_driver = { .name = DRV_NAME, - .pcm_new = lpass_platform_pcm_new, - .pcm_free = lpass_platform_pcm_free, - .ops = &lpass_platform_pcm_ops, + .open = lpass_platform_pcmops_open, + .close = lpass_platform_pcmops_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = lpass_platform_pcmops_hw_params, + .hw_free = lpass_platform_pcmops_hw_free, + .prepare = lpass_platform_pcmops_prepare, + .trigger = lpass_platform_pcmops_trigger, + .pointer = lpass_platform_pcmops_pointer, + .mmap = lpass_platform_pcmops_mmap, + .pcm_construct = lpass_platform_pcm_new, + .pcm_destruct = lpass_platform_pcm_free, + }; int asoc_qcom_lpass_platform_register(struct platform_device *pdev) diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 548eb4fa2da6..8150c10f081e 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -206,16 +206,16 @@ static void event_handler(uint32_t opcode, uint32_t token, } } -static int q6asm_dai_prepare(struct snd_pcm_substream *substream) +static int q6asm_dai_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; struct q6asm_dai_rtd *prtd = runtime->private_data; - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); struct q6asm_dai_data *pdata; int ret, i; - pdata = snd_soc_component_get_drvdata(c); + pdata = snd_soc_component_get_drvdata(component); if (!pdata) return -EINVAL; @@ -294,7 +294,8 @@ static int q6asm_dai_prepare(struct snd_pcm_substream *substream) return 0; } -static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) +static int q6asm_dai_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { int ret = 0; struct snd_pcm_runtime *runtime = substream->runtime; @@ -322,21 +323,21 @@ static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static int q6asm_dai_open(struct snd_pcm_substream *substream) +static int q6asm_dai_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai; - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); struct q6asm_dai_rtd *prtd; struct q6asm_dai_data *pdata; - struct device *dev = c->dev; + struct device *dev = component->dev; int ret = 0; int stream_id; stream_id = cpu_dai->driver->id; - pdata = snd_soc_component_get_drvdata(c); + pdata = snd_soc_component_get_drvdata(component); if (!pdata) { pr_err("Drv data not found ..\n"); return -EINVAL; @@ -414,7 +415,8 @@ static int q6asm_dai_open(struct snd_pcm_substream *substream) return 0; } -static int q6asm_dai_close(struct snd_pcm_substream *substream) +static int q6asm_dai_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; @@ -435,7 +437,8 @@ static int q6asm_dai_close(struct snd_pcm_substream *substream) return 0; } -static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -447,22 +450,21 @@ static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); } -static int q6asm_dai_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int q6asm_dai_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; - struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); - struct device *dev = c->dev; + struct device *dev = component->dev; return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); } -static int q6asm_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int q6asm_dai_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; @@ -482,17 +484,6 @@ static int q6asm_dai_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_pcm_ops q6asm_dai_ops = { - .open = q6asm_dai_open, - .hw_params = q6asm_dai_hw_params, - .close = q6asm_dai_close, - .ioctl = snd_pcm_lib_ioctl, - .prepare = q6asm_dai_prepare, - .trigger = q6asm_dai_trigger, - .pointer = q6asm_dai_pointer, - .mmap = q6asm_dai_mmap, -}; - static void compress_event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { @@ -635,8 +626,14 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); int dir = stream->direction; struct q6asm_dai_data *pdata; + struct q6asm_flac_cfg flac_cfg; struct device *dev = c->dev; int ret; + union snd_codec_options *codec_options; + struct snd_dec_flac *flac; + + codec_options = &(prtd->codec_param.codec.options); + memcpy(&prtd->codec_param, params, sizeof(*params)); @@ -673,6 +670,32 @@ static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, return ret; } + switch (params->codec.id) { + case SND_AUDIOCODEC_FLAC: + + memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); + flac = &codec_options->flac_d; + + flac_cfg.ch_cfg = params->codec.ch_in; + flac_cfg.sample_rate = params->codec.sample_rate; + flac_cfg.stream_info_present = 1; + flac_cfg.sample_size = flac->sample_size; + flac_cfg.min_blk_size = flac->min_blk_size; + flac_cfg.max_blk_size = flac->max_blk_size; + flac_cfg.max_frame_size = flac->max_frame_size; + flac_cfg.min_frame_size = flac->min_frame_size; + + ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + &flac_cfg); + if (ret < 0) { + dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); + return -EIO; + } + break; + default: + break; + } + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, (prtd->pcm_size / prtd->periods), prtd->periods); @@ -768,8 +791,9 @@ static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream, caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; - caps->num_codecs = 1; + caps->num_codecs = 2; caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_FLAC; return 0; } @@ -800,15 +824,15 @@ static struct snd_compr_ops q6asm_dai_compr_ops = { .ack = q6asm_dai_compr_ack, }; -static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int q6asm_dai_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm_substream *psubstream, *csubstream; - struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_pcm *pcm = rtd->pcm; struct device *dev; int size, ret; - dev = c->dev; + dev = component->dev; size = q6asm_dai_hardware_playback.buffer_bytes_max; psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (psubstream) { @@ -835,7 +859,8 @@ static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void q6asm_dai_pcm_free(struct snd_pcm *pcm) +static void q6asm_dai_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; int i; @@ -852,9 +877,16 @@ static void q6asm_dai_pcm_free(struct snd_pcm *pcm) static const struct snd_soc_component_driver q6asm_fe_dai_component = { .name = DRV_NAME, - .ops = &q6asm_dai_ops, - .pcm_new = q6asm_dai_pcm_new, - .pcm_free = q6asm_dai_pcm_free, + .open = q6asm_dai_open, + .hw_params = q6asm_dai_hw_params, + .close = q6asm_dai_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .prepare = q6asm_dai_prepare, + .trigger = q6asm_dai_trigger, + .pointer = q6asm_dai_pointer, + .mmap = q6asm_dai_mmap, + .pcm_construct = q6asm_dai_pcm_new, + .pcm_destruct = q6asm_dai_pcm_free, .compr_ops = &q6asm_dai_compr_ops, }; diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index e8141a33a55e..36e0eab13a98 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -38,6 +38,7 @@ #define ASM_SESSION_CMD_RUN_V2 0x00010DAA #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 #define ASM_MEDIA_FMT_MP3 0x00010BE9 +#define ASM_MEDIA_FMT_FLAC 0x00010C16 #define ASM_DATA_CMD_WRITE_V2 0x00010DAB #define ASM_DATA_CMD_READ_V2 0x00010DAC #define ASM_SESSION_CMD_SUSPEND 0x00010DEC @@ -89,6 +90,20 @@ struct asm_multi_channel_pcm_fmt_blk_v2 { u8 channel_mapping[PCM_MAX_NUM_CHANNEL]; } __packed; +struct asm_flac_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 is_stream_info_present; + u16 num_channels; + u16 min_blk_size; + u16 max_blk_size; + u16 md5_sum[8]; + u32 sample_rate; + u32 min_frame_size; + u32 max_frame_size; + u16 sample_size; + u16 reserved; +} __packed; + struct asm_stream_cmd_set_encdec_param { u32 param_id; u32 param_size; @@ -876,6 +891,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, case FORMAT_LINEAR_PCM: open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; + case SND_AUDIOCODEC_FLAC: + open->dec_fmt_id = ASM_MEDIA_FMT_FLAC; + break; default: dev_err(ac->dev, "Invalid format 0x%x\n", format); rc = -EINVAL; @@ -1021,6 +1039,42 @@ err: } EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); + +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct q6asm_flac_cfg *cfg) +{ + struct asm_flac_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + fmt->is_stream_info_present = cfg->stream_info_present; + fmt->num_channels = cfg->ch_cfg; + fmt->min_blk_size = cfg->min_blk_size; + fmt->max_blk_size = cfg->max_blk_size; + fmt->sample_rate = cfg->sample_rate; + fmt->min_frame_size = cfg->min_frame_size; + fmt->max_frame_size = cfg->max_frame_size; + fmt->sample_size = cfg->sample_size; + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * @@ -1075,6 +1129,7 @@ err: } EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); + /** * q6asm_read() - read data of period size from audio client * diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 9f5fb573e4a0..6764f55f7078 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -32,6 +32,19 @@ enum { #define NO_TIMESTAMP 0xFF00 #define FORMAT_LINEAR_PCM 0x0000 +struct q6asm_flac_cfg { + u32 sample_rate; + u32 ext_sample_rate; + u32 min_frame_size; + u32 max_frame_size; + u16 stream_info_present; + u16 min_blk_size; + u16 max_blk_size; + u16 ch_cfg; + u16 sample_size; + u16 md5_sum; +}; + typedef void (*q6asm_cb) (uint32_t opcode, uint32_t token, void *payload, void *priv); struct audio_client; @@ -54,6 +67,8 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample); +int q6asm_stream_media_format_block_flac(struct audio_client *ac, + struct q6asm_flac_cfg *cfg); int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index ddcd9978cf57..20724102e85a 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -939,12 +939,12 @@ static const struct snd_soc_dapm_route intercon[] = { }; -static int routing_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int routing_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct msm_routing_data *data = dev_get_drvdata(c->dev); + struct msm_routing_data *data = dev_get_drvdata(component->dev); unsigned int be_id = rtd->cpu_dai->id; struct session_data *session; int path_type; @@ -980,10 +980,6 @@ static int routing_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_pcm_ops q6pcm_routing_ops = { - .hw_params = routing_hw_params, -}; - static int msm_routing_probe(struct snd_soc_component *c) { int i; @@ -997,9 +993,9 @@ static int msm_routing_probe(struct snd_soc_component *c) } static const struct snd_soc_component_driver msm_soc_routing_component = { - .ops = &q6pcm_routing_ops, .probe = msm_routing_probe, .name = DRV_NAME, + .hw_params = routing_hw_params, .dapm_widgets = msm_qdsp6_widgets, .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets), .dapm_routes = intercon, diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index b43657e6e655..d610b553ea3b 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -40,9 +40,10 @@ config SND_SOC_ROCKCHIP_MAX98090 select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E + select SND_SOC_HDMI_CODEC help Say Y or M here if you want to add support for SoC audio on Rockchip - boards using the MAX98090 codec, such as Veyron. + boards using the MAX98090 codec and HDMI codec, such as Veyron. config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index e80b09143b63..60930fa85aa4 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -6,11 +6,13 @@ */ #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <sound/core.h> +#include <sound/hdmi-codec.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -36,28 +38,73 @@ static struct snd_soc_jack_pin headset_jack_pins[] = { }; -static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), +#define RK_MAX98090_WIDGETS \ + SND_SOC_DAPM_HP("Headphone", NULL), \ + SND_SOC_DAPM_MIC("Headset Mic", NULL), \ + SND_SOC_DAPM_MIC("Int Mic", NULL), \ + SND_SOC_DAPM_SPK("Speaker", NULL) + +#define RK_HDMI_WIDGETS \ + SND_SOC_DAPM_LINE("HDMI", NULL) + +static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = { + RK_MAX98090_WIDGETS, +}; + +static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = { + RK_HDMI_WIDGETS, +}; + +static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = { + RK_MAX98090_WIDGETS, + RK_HDMI_WIDGETS, +}; + +#define RK_MAX98090_AUDIO_MAP \ + {"IN34", NULL, "Headset Mic"}, \ + {"Headset Mic", NULL, "MICBIAS"}, \ + {"DMICL", NULL, "Int Mic"}, \ + {"Headphone", NULL, "HPL"}, \ + {"Headphone", NULL, "HPR"}, \ + {"Speaker", NULL, "SPKL"}, \ + {"Speaker", NULL, "SPKR"} + +#define RK_HDMI_AUDIO_MAP \ + {"HDMI", NULL, "TX"} + +static const struct snd_soc_dapm_route rk_max98090_audio_map[] = { + RK_MAX98090_AUDIO_MAP, +}; + +static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = { + RK_HDMI_AUDIO_MAP, +}; + +static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = { + RK_MAX98090_AUDIO_MAP, + RK_HDMI_AUDIO_MAP, +}; + +#define RK_MAX98090_CONTROLS \ + SOC_DAPM_PIN_SWITCH("Headphone"), \ + SOC_DAPM_PIN_SWITCH("Headset Mic"), \ + SOC_DAPM_PIN_SWITCH("Int Mic"), \ + SOC_DAPM_PIN_SWITCH("Speaker") + +#define RK_HDMI_CONTROLS \ + SOC_DAPM_PIN_SWITCH("HDMI") + +static const struct snd_kcontrol_new rk_max98090_controls[] = { + RK_MAX98090_CONTROLS, }; -static const struct snd_soc_dapm_route rk_audio_map[] = { - {"IN34", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "MICBIAS"}, - {"DMICL", NULL, "Int Mic"}, - {"Headphone", NULL, "HPL"}, - {"Headphone", NULL, "HPR"}, - {"Speaker", NULL, "SPKL"}, - {"Speaker", NULL, "SPKR"}, +static const struct snd_kcontrol_new rk_hdmi_controls[] = { + RK_HDMI_CONTROLS, }; -static const struct snd_kcontrol_new rk_mc_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Speaker"), +static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = { + RK_MAX98090_CONTROLS, + RK_HDMI_CONTROLS, }; static int rk_jack_event(struct notifier_block *nb, unsigned long event, @@ -125,15 +172,20 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + if (ret) { + dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret); return ret; } ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); + + /* HDMI codec dai does not need to set sysclk. */ + if (!strcmp(rtd->dai_link->name, "HDMI")) + return 0; + + if (ret) { + dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret); return ret; } @@ -155,20 +207,88 @@ static const struct snd_soc_ops rk_aif1_ops = { .startup = rk_aif1_startup, }; -SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -static struct snd_soc_dai_link rk_dailink = { - .name = "max98090", - .stream_name = "Audio", - .init = rk_init, - .ops = &rk_aif1_ops, - /* set max98090 as slave */ - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - SND_SOC_DAILINK_REG(hifi), +SND_SOC_DAILINK_DEFS(analog, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(hdmi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +enum { + DAILINK_MAX98090, + DAILINK_HDMI, +}; + +static struct snd_soc_jack rk_hdmi_jack; + +static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_component *component = runtime->codec_dai->component; + int ret; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &rk_hdmi_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + + return hdmi_codec_set_jack_detect(component, &rk_hdmi_jack); +} + +/* max98090 dai_link */ +static struct snd_soc_dai_link rk_max98090_dailinks[] = { + { + .name = "max98090", + .stream_name = "Analog", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set max98090 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(analog), + }, +}; + +/* HDMI codec dai_link */ +static struct snd_soc_dai_link rk_hdmi_dailinks[] = { + { + .name = "HDMI", + .stream_name = "HDMI", + .init = rk_hdmi_init, + .ops = &rk_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(hdmi), + } +}; + +/* max98090 and HDMI codec dai_link */ +static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = { + [DAILINK_MAX98090] = { + .name = "max98090", + .stream_name = "Analog", + .init = rk_init, + .ops = &rk_aif1_ops, + /* set max98090 as slave */ + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(analog), + }, + [DAILINK_HDMI] = { + .name = "HDMI", + .stream_name = "HDMI", + .init = rk_hdmi_init, + .ops = &rk_aif1_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(hdmi), + } }; static int rk_98090_headset_init(struct snd_soc_component *component); @@ -178,19 +298,47 @@ static struct snd_soc_aux_dev rk_98090_headset_dev = { .init = rk_98090_headset_init, }; -static struct snd_soc_card snd_soc_card_rk = { +static struct snd_soc_card rockchip_max98090_card = { .name = "ROCKCHIP-I2S", .owner = THIS_MODULE, - .dai_link = &rk_dailink, - .num_links = 1, + .dai_link = rk_max98090_dailinks, + .num_links = ARRAY_SIZE(rk_max98090_dailinks), + .aux_dev = &rk_98090_headset_dev, + .num_aux_devs = 1, + .dapm_widgets = rk_max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets), + .dapm_routes = rk_max98090_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map), + .controls = rk_max98090_controls, + .num_controls = ARRAY_SIZE(rk_max98090_controls), +}; + +static struct snd_soc_card rockchip_hdmi_card = { + .name = "ROCKCHIP-HDMI", + .owner = THIS_MODULE, + .dai_link = rk_hdmi_dailinks, + .num_links = ARRAY_SIZE(rk_hdmi_dailinks), + .dapm_widgets = rk_hdmi_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets), + .dapm_routes = rk_hdmi_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map), + .controls = rk_hdmi_controls, + .num_controls = ARRAY_SIZE(rk_hdmi_controls), +}; + +static struct snd_soc_card rockchip_max98090_hdmi_card = { + .name = "ROCKCHIP-MAX98090-HDMI", + .owner = THIS_MODULE, + .dai_link = rk_max98090_hdmi_dailinks, + .num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks), .aux_dev = &rk_98090_headset_dev, .num_aux_devs = 1, - .dapm_widgets = rk_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets), - .dapm_routes = rk_audio_map, - .num_dapm_routes = ARRAY_SIZE(rk_audio_map), - .controls = rk_mc_controls, - .num_controls = ARRAY_SIZE(rk_mc_controls), + .dapm_widgets = rk_max98090_hdmi_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets), + .dapm_routes = rk_max98090_hdmi_audio_map, + .num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map), + .controls = rk_max98090_hdmi_controls, + .num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls), }; static int rk_98090_headset_init(struct snd_soc_component *component) @@ -198,7 +346,7 @@ static int rk_98090_headset_init(struct snd_soc_component *component) int ret; /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack", + ret = snd_soc_card_jack_new(component->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, @@ -213,41 +361,75 @@ static int rk_98090_headset_init(struct snd_soc_component *component) return ret; } +static int rk_parse_headset_from_of(struct device *dev, struct device_node *np) +{ + rk_98090_headset_dev.dlc.of_node = of_parse_phandle( + np, "rockchip,headset-codec", 0); + if (!rk_98090_headset_dev.dlc.of_node) { + dev_err(dev, + "Property 'rockchip,headset-codec' missing/invalid\n"); + return -EINVAL; + } + return 0; +} + static int snd_rk_mc_probe(struct platform_device *pdev) { int ret = 0; - struct snd_soc_card *card = &snd_soc_card_rk; + struct snd_soc_card *card; + struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; + struct device_node *np_cpu; + struct device_node *np_audio, *np_hdmi; - /* register the soc card */ - card->dev = &pdev->dev; + /* Parse DTS for I2S controller. */ + np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0); - rk_dailink.codecs->of_node = of_parse_phandle(np, - "rockchip,audio-codec", 0); - if (!rk_dailink.codecs->of_node) { + if (!np_cpu) { dev_err(&pdev->dev, - "Property 'rockchip,audio-codec' missing or invalid\n"); + "Property 'rockchip,i2s-controller missing or invalid\n"); return -EINVAL; } - rk_dailink.cpus->of_node = of_parse_phandle(np, - "rockchip,i2s-controller", 0); - if (!rk_dailink.cpus->of_node) { - dev_err(&pdev->dev, - "Property 'rockchip,i2s-controller' missing or invalid\n"); + /* + * Find the card to use based on the presences of audio codec + * and hdmi codec in device property. Set their of_node accordingly. + */ + np_audio = of_parse_phandle(np, "rockchip,audio-codec", 0); + np_hdmi = of_parse_phandle(np, "rockchip,hdmi-codec", 0); + if (np_audio && np_hdmi) { + card = &rockchip_max98090_hdmi_card; + card->dai_link[DAILINK_MAX98090].codecs->of_node = np_audio; + card->dai_link[DAILINK_HDMI].codecs->of_node = np_hdmi; + card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu; + card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu; + card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu; + card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu; + } else if (np_audio) { + card = &rockchip_max98090_card; + card->dai_link[0].codecs->of_node = np_audio; + card->dai_link[0].cpus->of_node = np_cpu; + card->dai_link[0].platforms->of_node = np_cpu; + } else if (np_hdmi) { + card = &rockchip_hdmi_card; + card->dai_link[0].codecs->of_node = np_hdmi; + card->dai_link[0].cpus->of_node = np_cpu; + card->dai_link[0].platforms->of_node = np_cpu; + } else { + dev_err(dev, "At least one of codecs should be specified\n"); return -EINVAL; } - rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + card->dev = dev; - rk_98090_headset_dev.dlc.of_node = of_parse_phandle(np, - "rockchip,headset-codec", 0); - if (!rk_98090_headset_dev.dlc.of_node) { - dev_err(&pdev->dev, - "Property 'rockchip,headset-codec' missing/invalid\n"); - return -EINVAL; + /* Parse headset detection codec. */ + if (np_audio) { + ret = rk_parse_headset_from_of(dev, np); + if (ret) + return ret; } + /* Parse card name. */ ret = snd_soc_of_parse_card_name(card, "rockchip,model"); if (ret) { dev_err(&pdev->dev, @@ -255,6 +437,7 @@ static int snd_rk_mc_probe(struct platform_device *pdev) return ret; } + /* register the soc card */ ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 638983123d8f..1a0b163ca47b 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -194,11 +194,13 @@ config SND_SOC_ODROID help Say Y here to enable audio support for the Odroid XU3/XU4. -config SND_SOC_ARNDALE_RT5631_ALC5631 - tristate "Audio support for RT5631(ALC5631) on Arndale Board" - depends on I2C - select SND_SAMSUNG_I2S - select SND_SOC_RT5631 +config SND_SOC_ARNDALE + tristate "Audio support for Arndale Board" + depends on I2C + select SND_SAMSUNG_I2S + select SND_SOC_RT5631 + select MFD_WM8994 + select SND_SOC_WM8994 config SND_SOC_SAMSUNG_TM2_WM5110 tristate "SoC I2S Audio support for WM5110 on TM2 board" diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index c3b76035f69c..8f5dfe20b9f1 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -39,7 +39,7 @@ snd-soc-lowland-objs := lowland.o snd-soc-littlemill-objs := littlemill.o snd-soc-bells-objs := bells.o snd-soc-odroid-objs := odroid.o -snd-soc-arndale-rt5631-objs := arndale_rt5631.o +snd-soc-arndale-objs := arndale.o snd-soc-tm2-wm5110-objs := tm2_wm5110.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o @@ -62,5 +62,5 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o obj-$(CONFIG_SND_SOC_ODROID) += snd-soc-odroid.o -obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o +obj-$(CONFIG_SND_SOC_ARNDALE) += snd-soc-arndale.o obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c new file mode 100644 index 000000000000..d64602950cbd --- /dev/null +++ b/sound/soc/samsung/arndale.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2014, Insignal Co., Ltd. +// +// Author: Claude <claude@insginal.co.kr> + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "../codecs/wm8994.h" +#include "i2s.h" + +static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int rfs, ret; + unsigned long rclk; + + rfs = 256; + + rclk = params_rate(params) * rfs; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, + 0, SND_SOC_CLOCK_OUT); + + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops arndale_rt5631_ops = { + .hw_params = arndale_rt5631_hw_params, +}; + +static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int rfs, rclk; + + /* Ensure AIF1CLK is >= 3 MHz for optimal performance */ + if (params_width(params) == 24) + rfs = 384; + else if (params_rate(params) == 8000 || params_rate(params) == 11025) + rfs = 512; + else + rfs = 256; + + rclk = params_rate(params) * rfs; + + /* + * We add 1 to the frequency value to ensure proper EPLL setting + * for each audio sampling rate (see epll_24mhz_tbl in drivers/clk/ + * samsung/clk-exynos5250.c for list of available EPLL rates). + * The CODEC uses clk API and the value will be rounded hence the MCLK1 + * clock's frequency will still be exact multiple of the sample rate. + */ + return snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, + rclk + 1, SND_SOC_CLOCK_IN); +} + +static struct snd_soc_ops arndale_wm1811_ops = { + .hw_params = arndale_wm1811_hw_params, +}; + +SND_SOC_DAILINK_DEFS(rt5631_hifi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-aif1")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link arndale_rt5631_dai[] = { + { + .name = "RT5631 HiFi", + .stream_name = "Primary", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .ops = &arndale_rt5631_ops, + SND_SOC_DAILINK_REG(rt5631_hifi), + }, +}; + +SND_SOC_DAILINK_DEFS(wm1811_hifi, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link arndale_wm1811_dai[] = { + { + .name = "WM1811 HiFi", + .stream_name = "Primary", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ops = &arndale_wm1811_ops, + SND_SOC_DAILINK_REG(wm1811_hifi), + }, +}; + +static struct snd_soc_card arndale_rt5631 = { + .name = "Arndale RT5631", + .owner = THIS_MODULE, + .dai_link = arndale_rt5631_dai, + .num_links = ARRAY_SIZE(arndale_rt5631_dai), +}; + +static struct snd_soc_card arndale_wm1811 = { + .name = "Arndale WM1811", + .owner = THIS_MODULE, + .dai_link = arndale_wm1811_dai, + .num_links = ARRAY_SIZE(arndale_wm1811_dai), +}; + +static void arndale_put_of_nodes(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int i; + + for_each_card_prelinks(card, i, dai_link) { + of_node_put(dai_link->cpus->of_node); + of_node_put(dai_link->codecs->of_node); + } +} + +static int arndale_audio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card; + struct snd_soc_dai_link *dai_link; + int ret; + + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + card->dev = &pdev->dev; + dai_link = card->dai_link; + + dai_link->cpus->of_node = of_parse_phandle(np, "samsung,audio-cpu", 0); + if (!dai_link->cpus->of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-cpu' missing or invalid\n"); + return -EINVAL; + } + + if (!dai_link->platforms->name) + dai_link->platforms->of_node = dai_link->cpus->of_node; + + dai_link->codecs->of_node = of_parse_phandle(np, "samsung,audio-codec", 0); + if (!dai_link->codecs->of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err_put_of_nodes; + } + + ret = devm_snd_soc_register_card(card->dev, card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); + goto err_put_of_nodes; + } + return 0; + +err_put_of_nodes: + arndale_put_of_nodes(card); + return ret; +} + +static int arndale_audio_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + arndale_put_of_nodes(card); + return 0; +} + +static const struct of_device_id arndale_audio_of_match[] = { + { .compatible = "samsung,arndale-rt5631", .data = &arndale_rt5631 }, + { .compatible = "samsung,arndale-alc5631", .data = &arndale_rt5631 }, + { .compatible = "samsung,arndale-wm1811", .data = &arndale_wm1811 }, + {}, +}; +MODULE_DEVICE_TABLE(of, arndale_audio_of_match); + +static struct platform_driver arndale_audio_driver = { + .driver = { + .name = "arndale-audio", + .pm = &snd_soc_pm_ops, + .of_match_table = arndale_audio_of_match, + }, + .probe = arndale_audio_probe, + .remove = arndale_audio_remove, +}; + +module_platform_driver(arndale_audio_driver); + +MODULE_AUTHOR("Claude <claude@insignal.co.kr>"); +MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c deleted file mode 100644 index fd8c6642fb0d..000000000000 --- a/sound/soc/samsung/arndale_rt5631.c +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// Copyright (c) 2014, Insignal Co., Ltd. -// -// Author: Claude <claude@insginal.co.kr> - -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include "i2s.h" - -static int arndale_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int rfs, ret; - unsigned long rclk; - - rfs = 256; - - rclk = params_rate(params) * rfs; - - ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, - 0, SND_SOC_CLOCK_OUT); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, - 0, SND_SOC_CLOCK_OUT); - - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops arndale_ops = { - .hw_params = arndale_hw_params, -}; - -SND_SOC_DAILINK_DEFS(rt5631_hifi, - DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-hifi")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -static struct snd_soc_dai_link arndale_rt5631_dai[] = { - { - .name = "RT5631 HiFi", - .stream_name = "Primary", - .dai_fmt = SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, - .ops = &arndale_ops, - SND_SOC_DAILINK_REG(rt5631_hifi), - }, -}; - -static struct snd_soc_card arndale_rt5631 = { - .name = "Arndale RT5631", - .owner = THIS_MODULE, - .dai_link = arndale_rt5631_dai, - .num_links = ARRAY_SIZE(arndale_rt5631_dai), -}; - -static void arndale_put_of_nodes(struct snd_soc_card *card) -{ - struct snd_soc_dai_link *dai_link; - int i; - - for_each_card_prelinks(card, i, dai_link) { - of_node_put(dai_link->cpus->of_node); - of_node_put(dai_link->codecs->of_node); - } -} - -static int arndale_audio_probe(struct platform_device *pdev) -{ - int n, ret; - struct device_node *np = pdev->dev.of_node; - struct snd_soc_card *card = &arndale_rt5631; - - card->dev = &pdev->dev; - - for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) { - if (!arndale_rt5631_dai[n].cpus->dai_name) { - arndale_rt5631_dai[n].cpus->of_node = of_parse_phandle(np, - "samsung,audio-cpu", n); - - if (!arndale_rt5631_dai[n].cpus->of_node) { - dev_err(&pdev->dev, - "Property 'samsung,audio-cpu' missing or invalid\n"); - return -EINVAL; - } - } - if (!arndale_rt5631_dai[n].platforms->name) - arndale_rt5631_dai[n].platforms->of_node = - arndale_rt5631_dai[n].cpus->of_node; - - arndale_rt5631_dai[n].codecs->name = NULL; - arndale_rt5631_dai[n].codecs->of_node = of_parse_phandle(np, - "samsung,audio-codec", n); - if (!arndale_rt5631_dai[0].codecs->of_node) { - dev_err(&pdev->dev, - "Property 'samsung,audio-codec' missing or invalid\n"); - ret = -EINVAL; - goto err_put_of_nodes; - } - } - - ret = devm_snd_soc_register_card(card->dev, card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); - goto err_put_of_nodes; - } - return 0; - -err_put_of_nodes: - arndale_put_of_nodes(card); - return ret; -} - -static int arndale_audio_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - arndale_put_of_nodes(card); - return 0; -} - -static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = { - { .compatible = "samsung,arndale-rt5631", }, - { .compatible = "samsung,arndale-alc5631", }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match); - -static struct platform_driver arndale_audio_driver = { - .driver = { - .name = "arndale-audio", - .pm = &snd_soc_pm_ops, - .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), - }, - .probe = arndale_audio_probe, - .remove = arndale_audio_remove, -}; - -module_platform_driver(arndale_audio_driver); - -MODULE_AUTHOR("Claude <claude@insignal.co.kr>"); -MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index 65497cd477a5..294dce111b05 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -137,8 +137,9 @@ static void idma_done(void *id, int bytes_xfer) snd_pcm_period_elapsed(substream); } -static int idma_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int idma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct idma_ctrl *prtd = substream->runtime->private_data; @@ -163,14 +164,16 @@ static int idma_hw_params(struct snd_pcm_substream *substream, return 0; } -static int idma_hw_free(struct snd_pcm_substream *substream) +static int idma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); return 0; } -static int idma_prepare(struct snd_pcm_substream *substream) +static int idma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct idma_ctrl *prtd = substream->runtime->private_data; @@ -183,7 +186,8 @@ static int idma_prepare(struct snd_pcm_substream *substream) return 0; } -static int idma_trigger(struct snd_pcm_substream *substream, int cmd) +static int idma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct idma_ctrl *prtd = substream->runtime->private_data; int ret = 0; @@ -216,7 +220,8 @@ static int idma_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t - idma_pointer(struct snd_pcm_substream *substream) +idma_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct idma_ctrl *prtd = runtime->private_data; @@ -233,7 +238,8 @@ static snd_pcm_uframes_t return bytes_to_frames(substream->runtime, res); } -static int idma_mmap(struct snd_pcm_substream *substream, +static int idma_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -278,7 +284,8 @@ static irqreturn_t iis_irq(int irqno, void *dev_id) return IRQ_HANDLED; } -static int idma_open(struct snd_pcm_substream *substream) +static int idma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct idma_ctrl *prtd; @@ -304,7 +311,8 @@ static int idma_open(struct snd_pcm_substream *substream) return 0; } -static int idma_close(struct snd_pcm_substream *substream) +static int idma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct idma_ctrl *prtd = runtime->private_data; @@ -319,19 +327,8 @@ static int idma_close(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops idma_ops = { - .open = idma_open, - .close = idma_close, - .ioctl = snd_pcm_lib_ioctl, - .trigger = idma_trigger, - .pointer = idma_pointer, - .mmap = idma_mmap, - .hw_params = idma_hw_params, - .hw_free = idma_hw_free, - .prepare = idma_prepare, -}; - -static void idma_free(struct snd_pcm *pcm) +static void idma_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -367,7 +364,8 @@ static int preallocate_idma_buffer(struct snd_pcm *pcm, int stream) return 0; } -static int idma_new(struct snd_soc_pcm_runtime *rtd) +static int idma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -394,9 +392,17 @@ void idma_reg_addr_init(void __iomem *regs, dma_addr_t addr) EXPORT_SYMBOL_GPL(idma_reg_addr_init); static const struct snd_soc_component_driver asoc_idma_platform = { - .ops = &idma_ops, - .pcm_new = idma_new, - .pcm_free = idma_free, + .open = idma_open, + .close = idma_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .trigger = idma_trigger, + .pointer = idma_pointer, + .mmap = idma_mmap, + .hw_params = idma_hw_params, + .hw_free = idma_hw_free, + .prepare = idma_prepare, + .pcm_construct = idma_new, + .pcm_destruct = idma_free, }; static int asoc_idma_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 5aee11c94f2a..93bb80d089be 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -115,7 +115,8 @@ static void camelot_rxdma(void *data) snd_pcm_period_elapsed(cam->rx_ss); } -static int camelot_pcm_open(struct snd_pcm_substream *substream) +static int camelot_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; @@ -148,7 +149,8 @@ static int camelot_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int camelot_pcm_close(struct snd_pcm_substream *substream) +static int camelot_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; @@ -168,7 +170,8 @@ static int camelot_pcm_close(struct snd_pcm_substream *substream) return 0; } -static int camelot_hw_params(struct snd_pcm_substream *substream, +static int camelot_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -191,12 +194,14 @@ static int camelot_hw_params(struct snd_pcm_substream *substream, return 0; } -static int camelot_hw_free(struct snd_pcm_substream *substream) +static int camelot_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static int camelot_prepare(struct snd_pcm_substream *substream) +static int camelot_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -244,7 +249,8 @@ static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam) BRGREG(BRGACR) = acr | ACR_RDS; } -static int camelot_trigger(struct snd_pcm_substream *substream, int cmd) +static int camelot_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct camelot_pcm *cam = &cam_pcm_data[rtd->cpu_dai->id]; @@ -270,7 +276,8 @@ static int camelot_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -292,18 +299,8 @@ static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, pos); } -static const struct snd_pcm_ops camelot_pcm_ops = { - .open = camelot_pcm_open, - .close = camelot_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = camelot_hw_params, - .hw_free = camelot_hw_free, - .prepare = camelot_prepare, - .trigger = camelot_trigger, - .pointer = camelot_pos, -}; - -static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int camelot_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; @@ -319,8 +316,15 @@ static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) } static const struct snd_soc_component_driver sh7760_soc_component = { - .ops = &camelot_pcm_ops, - .pcm_new = camelot_pcm_new, + .open = camelot_pcm_open, + .close = camelot_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = camelot_hw_params, + .hw_free = camelot_hw_free, + .prepare = camelot_prepare, + .trigger = camelot_trigger, + .pointer = camelot_pos, + .pcm_construct = camelot_pcm_new, }; static int sh7760_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3447dbdba1f1..e384fdc8d60e 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1718,7 +1718,8 @@ static const struct snd_pcm_hardware fsi_pcm_hardware = { .fifo_size = 256, }; -static int fsi_pcm_open(struct snd_pcm_substream *substream) +static int fsi_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; int ret = 0; @@ -1731,19 +1732,22 @@ static int fsi_pcm_open(struct snd_pcm_substream *substream) return ret; } -static int fsi_hw_params(struct snd_pcm_substream *substream, +static int fsi_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } -static int fsi_hw_free(struct snd_pcm_substream *substream) +static int fsi_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t fsi_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_stream *io = fsi_stream_get(fsi, substream); @@ -1751,14 +1755,6 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) return fsi_sample2frame(fsi, io->buff_sample_pos); } -static const struct snd_pcm_ops fsi_pcm_ops = { - .open = fsi_pcm_open, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = fsi_hw_params, - .hw_free = fsi_hw_free, - .pointer = fsi_pointer, -}; - /* * snd_soc_component */ @@ -1766,7 +1762,8 @@ static const struct snd_pcm_ops fsi_pcm_ops = { #define PREALLOC_BUFFER (32 * 1024) #define PREALLOC_BUFFER_MAX (32 * 1024) -static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int fsi_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { snd_pcm_lib_preallocate_pages_for_all( rtd->pcm, @@ -1817,8 +1814,12 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = { static const struct snd_soc_component_driver fsi_soc_component = { .name = "fsi", - .ops = &fsi_pcm_ops, - .pcm_new = fsi_pcm_new, + .open = fsi_pcm_open, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = fsi_hw_params, + .hw_free = fsi_hw_free, + .pointer = fsi_pointer, + .pcm_construct = fsi_pcm_new, }; /* diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a6c1cf987e6e..399dc6e9bde5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -302,7 +302,7 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, int rsnd_channel_normalization(int chan) { - if ((chan > 8) || (chan < 0)) + if (WARN_ON((chan > 8) || (chan < 0))) return 0; /* TDM Extend Mode needs 8ch */ @@ -1092,7 +1092,10 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, j++; } + of_node_put(node); } + + of_node_put(ssiu_np); } static void rsnd_parse_connect_simple(struct rsnd_priv *priv, @@ -1110,11 +1113,13 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv, struct device_node *endpoint) { struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint); + struct device_node *remote_node; if (!rsnd_io_to_mod_ssi(io)) return; + remote_node = of_graph_get_remote_port_parent(endpoint); + /* HDMI0 */ if (strstr(remote_node->full_name, "hdmi@fead0000")) { rsnd_flags_set(io, RSND_STREAM_HDMI0); @@ -1128,6 +1133,8 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv, } rsnd_parse_tdm_split_mode(priv, io, endpoint); + + of_node_put(remote_node); } void rsnd_parse_connect_common(struct rsnd_dai *rdai, @@ -1390,8 +1397,9 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) /* * pcm ops */ -static int rsnd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int rsnd_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); @@ -1438,7 +1446,8 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); } -static int rsnd_hw_free(struct snd_pcm_substream *substream) +static int rsnd_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); @@ -1452,7 +1461,8 @@ static int rsnd_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } -static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t rsnd_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); @@ -1464,13 +1474,6 @@ static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) return pointer; } -static const struct snd_pcm_ops rsnd_pcm_ops = { - .ioctl = snd_pcm_lib_ioctl, - .hw_params = rsnd_hw_params, - .hw_free = rsnd_hw_free, - .pointer = rsnd_pointer, -}; - /* * snd_kcontrol */ @@ -1664,8 +1667,11 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, * snd_soc_component */ static const struct snd_soc_component_driver rsnd_soc_component = { - .ops = &rsnd_pcm_ops, .name = "rsnd", + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = rsnd_hw_params, + .hw_free = rsnd_hw_free, + .pointer = rsnd_pointer, }; static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 28f65eba2bb4..95aa26d62e4f 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -165,14 +165,40 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; struct dma_slave_config cfg = {}; + enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; int is_play = rsnd_io_is_play(io); int ret; + /* + * in case of monaural data writing or reading through Audio-DMAC + * data is always in Left Justified format, so both src and dst + * DMA Bus width need to be set equal to physical data width. + */ + if (rsnd_runtime_channel_original(io) == 1) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int bits = snd_pcm_format_physical_width(runtime->format); + + switch (bits) { + case 8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dev, "invalid format width %d\n", bits); + return -EINVAL; + } + } + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; cfg.src_addr = dma->src_addr; cfg.dst_addr = dma->dst_addr; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_addr_width = buswidth; + cfg.dst_addr_width = buswidth; dev_dbg(dev, "%s %pad -> %pad\n", rsnd_mod_name(mod), diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 78c3145b4109..a5e21e554da2 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -281,7 +281,8 @@ static int siu_pcm_stmread_stop(struct siu_port *port_info) return 0; } -static int siu_pcm_hw_params(struct snd_pcm_substream *ss, +static int siu_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *ss, struct snd_pcm_hw_params *hw_params) { struct siu_info *info = siu_i2s_data; @@ -297,7 +298,8 @@ static int siu_pcm_hw_params(struct snd_pcm_substream *ss, return ret; } -static int siu_pcm_hw_free(struct snd_pcm_substream *ss) +static int siu_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct siu_info *info = siu_i2s_data; struct siu_port *port_info = siu_port_info(ss); @@ -324,11 +326,10 @@ static bool filter(struct dma_chan *chan, void *slave) return true; } -static int siu_pcm_open(struct snd_pcm_substream *ss) +static int siu_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { /* Playback / Capture */ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct siu_platform *pdata = component->dev->platform_data; struct siu_info *info = siu_i2s_data; struct siu_port *port_info = siu_port_info(ss); @@ -367,7 +368,8 @@ static int siu_pcm_open(struct snd_pcm_substream *ss) return 0; } -static int siu_pcm_close(struct snd_pcm_substream *ss) +static int siu_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct siu_info *info = siu_i2s_data; struct device *dev = ss->pcm->card->dev; @@ -389,7 +391,8 @@ static int siu_pcm_close(struct snd_pcm_substream *ss) return 0; } -static int siu_pcm_prepare(struct snd_pcm_substream *ss) +static int siu_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct siu_info *info = siu_i2s_data; struct siu_port *port_info = siu_port_info(ss); @@ -435,7 +438,8 @@ static int siu_pcm_prepare(struct snd_pcm_substream *ss) return 0; } -static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +static int siu_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *ss, int cmd) { struct siu_info *info = siu_i2s_data; struct device *dev = ss->pcm->card->dev; @@ -477,7 +481,9 @@ static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd) * So far only resolution of one period is supported, subject to extending the * dmangine API */ -static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss) +static snd_pcm_uframes_t +siu_pcm_pointer_dma(struct snd_soc_component *component, + struct snd_pcm_substream *ss) { struct device *dev = ss->pcm->card->dev; struct siu_info *info = siu_i2s_data; @@ -512,7 +518,8 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss) return bytes_to_frames(ss->runtime, ptr); } -static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int siu_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { /* card->dev == socdev->dev, see snd_soc_new_pcms() */ struct snd_card *card = rtd->card->snd_card; @@ -558,7 +565,8 @@ static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void siu_pcm_free(struct snd_pcm *pcm) +static void siu_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct platform_device *pdev = to_platform_device(pcm->card->dev); struct siu_port *port_info = siu_ports[pdev->id]; @@ -571,21 +579,17 @@ static void siu_pcm_free(struct snd_pcm *pcm) dev_dbg(pcm->card->dev, "%s\n", __func__); } -static const struct snd_pcm_ops siu_pcm_ops = { +struct const snd_soc_component_driver siu_component = { + .name = DRV_NAME, .open = siu_pcm_open, .close = siu_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = siu_pcm_hw_params, .hw_free = siu_pcm_hw_free, .prepare = siu_pcm_prepare, .trigger = siu_pcm_trigger, .pointer = siu_pcm_pointer_dma, -}; - -struct snd_soc_component_driver siu_component = { - .name = DRV_NAME, - .ops = &siu_pcm_ops, - .pcm_new = siu_pcm_new, - .pcm_free = siu_pcm_free, + .pcm_construct = siu_pcm_new, + .pcm_destruct = siu_pcm_free, }; EXPORT_SYMBOL_GPL(siu_component); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 79ffc2820ba9..1590e805d016 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -314,30 +314,24 @@ void snd_soc_component_module_put(struct snd_soc_component *component, int snd_soc_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->ops && - component->driver->ops->open) - return component->driver->ops->open(substream); - + if (component->driver->open) + return component->driver->open(component, substream); return 0; } int snd_soc_component_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->ops && - component->driver->ops->close) - return component->driver->ops->close(substream); - + if (component->driver->close) + return component->driver->close(component, substream); return 0; } int snd_soc_component_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->ops && - component->driver->ops->prepare) - return component->driver->ops->prepare(substream); - + if (component->driver->prepare) + return component->driver->prepare(component, substream); return 0; } @@ -345,20 +339,17 @@ int snd_soc_component_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - if (component->driver->ops && - component->driver->ops->hw_params) - return component->driver->ops->hw_params(substream, params); - + if (component->driver->hw_params) + return component->driver->hw_params(component, + substream, params); return 0; } int snd_soc_component_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->ops && - component->driver->ops->hw_free) - return component->driver->ops->hw_free(substream); - + if (component->driver->hw_free) + return component->driver->hw_free(component, substream); return 0; } @@ -366,10 +357,8 @@ int snd_soc_component_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { - if (component->driver->ops && - component->driver->ops->trigger) - return component->driver->ops->trigger(substream, cmd); - + if (component->driver->trigger) + return component->driver->trigger(component, substream, cmd); return 0; } @@ -431,14 +420,10 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream) struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* FIXME: use 1st pointer */ - if (component->driver->ops && - component->driver->ops->pointer) - return component->driver->ops->pointer(substream); - } + /* FIXME: use 1st pointer */ + for_each_rtd_components(rtd, rtdcom, component) + if (component->driver->pointer) + return component->driver->pointer(component, substream); return 0; } @@ -450,15 +435,11 @@ int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* FIXME: use 1st ioctl */ - if (component->driver->ops && - component->driver->ops->ioctl) - return component->driver->ops->ioctl(substream, - cmd, arg); - } + /* FIXME: use 1st ioctl */ + for_each_rtd_components(rtd, rtdcom, component) + if (component->driver->ioctl) + return component->driver->ioctl(component, substream, + cmd, arg); return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -471,15 +452,11 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream, struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* FIXME. it returns 1st copy now */ - if (component->driver->ops && - component->driver->ops->copy_user) - return component->driver->ops->copy_user( - substream, channel, pos, buf, bytes); - } + /* FIXME. it returns 1st copy now */ + for_each_rtd_components(rtd, rtdcom, component) + if (component->driver->copy_user) + return component->driver->copy_user( + component, substream, channel, pos, buf, bytes); return -EINVAL; } @@ -492,13 +469,11 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream, struct snd_soc_component *component; struct page *page; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* FIXME. it returns 1st page now */ - if (component->driver->ops && - component->driver->ops->page) { - page = component->driver->ops->page(substream, offset); + /* FIXME. it returns 1st page now */ + for_each_rtd_components(rtd, rtdcom, component) { + if (component->driver->page) { + page = component->driver->page(component, + substream, offset); if (page) return page; } @@ -514,30 +489,24 @@ int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream, struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* FIXME. it returns 1st mmap now */ - if (component->driver->ops && - component->driver->ops->mmap) - return component->driver->ops->mmap(substream, vma); - } + /* FIXME. it returns 1st mmap now */ + for_each_rtd_components(rtd, rtdcom, component) + if (component->driver->mmap) + return component->driver->mmap(component, + substream, vma); return -EINVAL; } -int snd_soc_pcm_component_new(struct snd_pcm *pcm) +int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = pcm->private_data; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; int ret; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (component->driver->pcm_new) { - ret = component->driver->pcm_new(rtd); + for_each_rtd_components(rtd, rtdcom, component) { + if (component->driver->pcm_construct) { + ret = component->driver->pcm_construct(component, rtd); if (ret < 0) return ret; } @@ -546,16 +515,12 @@ int snd_soc_pcm_component_new(struct snd_pcm *pcm) return 0; } -void snd_soc_pcm_component_free(struct snd_pcm *pcm) +void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = pcm->private_data; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - if (component->driver->pcm_free) - component->driver->pcm_free(pcm); - } + for_each_rtd_components(rtd, rtdcom, component) + if (component->driver->pcm_destruct) + component->driver->pcm_destruct(component, rtd->pcm); } diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 9e54d8ae6d2c..61f230324164 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -28,9 +28,7 @@ static int soc_compr_components_open(struct snd_compr_stream *cstream, struct snd_soc_rtdcom_list *rtdcom; int ret; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->open) continue; @@ -57,9 +55,7 @@ static int soc_compr_components_free(struct snd_compr_stream *cstream, struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (component == last) break; @@ -353,9 +349,7 @@ static int soc_compr_components_trigger(struct snd_compr_stream *cstream, struct snd_soc_rtdcom_list *rtdcom; int ret; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->trigger) continue; @@ -458,9 +452,7 @@ static int soc_compr_components_set_params(struct snd_compr_stream *cstream, struct snd_soc_rtdcom_list *rtdcom; int ret; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; @@ -601,9 +593,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_params) continue; @@ -627,9 +617,7 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_caps) continue; @@ -652,9 +640,7 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_codec_caps) continue; @@ -684,9 +670,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->ack) continue; @@ -715,9 +699,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->pointer) continue; @@ -740,9 +722,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; @@ -770,9 +750,7 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->set_metadata) continue; @@ -801,9 +779,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->get_metadata) continue; @@ -932,9 +908,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 88978a3036c4..062653ab03a3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -125,6 +125,9 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); + if (!rtd) + return 0; + if (attr == &dev_attr_pmdown_time.attr) return attr->mode; /* always visible */ return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */ @@ -274,43 +277,58 @@ static inline void snd_soc_debugfs_exit(void) #endif +/* + * This is glue code between snd_pcm_lib_ioctl() and + * snd_soc_component_driver :: ioctl + */ +int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} +EXPORT_SYMBOL_GPL(snd_soc_pcm_lib_ioctl); + static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component) { struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *comp; - for_each_rtdcom(rtd, rtdcom) { + for_each_rtd_components(rtd, rtdcom, comp) { /* already connected */ - if (rtdcom->component == component) + if (comp == component) return 0; } - rtdcom = kmalloc(sizeof(*rtdcom), GFP_KERNEL); + /* + * created rtdcom here will be freed when rtd->dev was freed. + * see + * soc_free_pcm_runtime() :: device_unregister(rtd->dev) + */ + rtdcom = devm_kzalloc(rtd->dev, sizeof(*rtdcom), GFP_KERNEL); if (!rtdcom) return -ENOMEM; rtdcom->component = component; INIT_LIST_HEAD(&rtdcom->list); + /* + * When rtd was freed, created rtdcom here will be + * also freed. + * And we don't need to call list_del(&rtdcom->list) + * when freed, because rtd is also freed. + */ list_add_tail(&rtdcom->list, &rtd->component_list); return 0; } -static void snd_soc_rtdcom_del_all(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; - - for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) - kfree(rtdcom1); - - INIT_LIST_HEAD(&rtd->component_list); -} - struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, const char *driver_name) { struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; if (!driver_name) return NULL; @@ -323,8 +341,8 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, * But, if many components which have same driver name are connected * to 1 rtd, this function will return 1st found component. */ - for_each_rtdcom(rtd, rtdcom) { - const char *component_name = rtdcom->component->driver->name; + for_each_rtd_components(rtd, rtdcom, component) { + const char *component_name = component->driver->name; if (!component_name) continue; @@ -338,6 +356,39 @@ struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); +static struct snd_soc_component +*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name) +{ + struct snd_soc_component *component; + struct snd_soc_component *found_component; + + found_component = NULL; + for_each_component(component) { + if ((dev == component->dev) && + (!driver_name || + (driver_name == component->driver->name) || + (strcmp(component->driver->name, driver_name) == 0))) { + found_component = component; + break; + } + } + + return found_component; +} + +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name) +{ + struct snd_soc_component *component; + + mutex_lock(&client_mutex); + component = snd_soc_lookup_component_nolocked(dev, driver_name); + mutex_unlock(&client_mutex); + + return component; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component); + struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream) { @@ -355,58 +406,104 @@ EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); static const struct snd_soc_ops null_snd_soc_ops; +static void soc_release_rtd_dev(struct device *dev) +{ + /* "dev" means "rtd->dev" */ + kfree(dev); +} + +static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) +{ + if (!rtd) + return; + + list_del(&rtd->list); + + flush_delayed_work(&rtd->delayed_work); + snd_soc_pcm_component_free(rtd); + + /* + * we don't need to call kfree() for rtd->dev + * see + * soc_release_rtd_dev() + * + * We don't need rtd->dev NULL check, because + * it is alloced *before* rtd. + * see + * soc_new_pcm_runtime() + */ + device_unregister(rtd->dev); +} + static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; + struct device *dev; + int ret; - rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); - if (!rtd) + /* + * for rtd->dev + */ + dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev) return NULL; - INIT_LIST_HEAD(&rtd->component_list); - rtd->card = card; - rtd->dai_link = dai_link; - if (!rtd->dai_link->ops) - rtd->dai_link->ops = &null_snd_soc_ops; + dev->parent = card->dev; + dev->release = soc_release_rtd_dev; + dev->groups = soc_dev_attr_groups; - rtd->codec_dais = kcalloc(dai_link->num_codecs, - sizeof(struct snd_soc_dai *), - GFP_KERNEL); - if (!rtd->codec_dais) { - kfree(rtd); + dev_set_name(dev, "%s", dai_link->name); + + ret = device_register(dev); + if (ret < 0) { + put_device(dev); /* soc_release_rtd_dev */ return NULL; } - return rtd; -} + /* + * for rtd + */ + rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); + if (!rtd) + goto free_rtd; -static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) -{ - kfree(rtd->codec_dais); - snd_soc_rtdcom_del_all(rtd); - kfree(rtd); -} + rtd->dev = dev; + dev_set_drvdata(dev, rtd); + + /* + * for rtd->codec_dais + */ + rtd->codec_dais = devm_kcalloc(dev, dai_link->num_codecs, + sizeof(struct snd_soc_dai *), + GFP_KERNEL); + if (!rtd->codec_dais) + goto free_rtd; + + /* + * rtd remaining settings + */ + INIT_LIST_HEAD(&rtd->component_list); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); + INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); + + rtd->card = card; + rtd->dai_link = dai_link; + if (!rtd->dai_link->ops) + rtd->dai_link->ops = &null_snd_soc_ops; -static void soc_add_pcm_runtime(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd) -{ /* see for_each_card_rtds */ list_add_tail(&rtd->list, &card->rtd_list); rtd->num = card->num_rtd; card->num_rtd++; -} -static void soc_remove_pcm_runtimes(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd, *_rtd; - - for_each_card_rtds_safe(card, rtd, _rtd) { - list_del(&rtd->list); - soc_free_pcm_runtime(rtd); - } + return rtd; - card->num_rtd = 0; +free_rtd: + soc_free_pcm_runtime(rtd); + return NULL; } struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, @@ -859,37 +956,168 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); -static bool soc_is_dai_link_bound(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) +static int soc_dai_link_sanity_check(struct snd_soc_card *card, + struct snd_soc_dai_link *link) { - struct snd_soc_pcm_runtime *rtd; + int i; + struct snd_soc_dai_link_component *codec, *platform; - for_each_card_rtds(card, rtd) { - if (rtd->dai_link == dai_link) - return true; + for_each_link_codecs(link, i, codec) { + /* + * Codec must be specified by 1 of name or OF node, + * not both or neither. + */ + if (!!codec->name == !!codec->of_node) { + dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + /* Codec DAI name must be specified */ + if (!codec->dai_name) { + dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", + link->name); + return -EINVAL; + } + + /* + * Defer card registration if codec component is not added to + * component list. + */ + if (!soc_find_component(codec)) + return -EPROBE_DEFER; } - return false; + for_each_link_platforms(link, i, platform) { + /* + * Platform may be specified by either name or OF node, but it + * can be left unspecified, then no components will be inserted + * in the rtdcom list + */ + if (!!platform->name == !!platform->of_node) { + dev_err(card->dev, + "ASoC: Neither/both platform name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + /* + * Defer card registration if platform component is not added to + * component list. + */ + if (!soc_find_component(platform)) + return -EPROBE_DEFER; + } + + /* FIXME */ + if (link->num_cpus > 1) { + dev_err(card->dev, + "ASoC: multi cpu is not yet supported %s\n", + link->name); + return -EINVAL; + } + + /* + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (link->cpus->name && link->cpus->of_node) { + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + /* + * Defer card registration if cpu dai component is not added to + * component list. + */ + if ((link->cpus->of_node || link->cpus->name) && + !soc_find_component(link->cpus)) + return -EPROBE_DEFER; + + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified + */ + if (!link->cpus->dai_name && + !(link->cpus->name || link->cpus->of_node)) { + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + return 0; +} + +/** + * snd_soc_remove_dai_link - Remove a DAI link from the list + * @card: The ASoC card that owns the link + * @dai_link: The DAI link to remove + * + * This function removes a DAI link from the ASoC card's link list. + * + * For DAI links previously added by topology, topology should + * remove them by using the dobj embedded in the link. + */ +void snd_soc_remove_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_pcm_runtime *rtd; + + lockdep_assert_held(&client_mutex); + + /* + * Notify the machine driver for extra destruction + */ + if (card->remove_dai_link) + card->remove_dai_link(card, dai_link); + + list_del(&dai_link->list); + + rtd = snd_soc_get_pcm_runtime(card, dai_link->name); + if (rtd) + soc_free_pcm_runtime(rtd); } +EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); -static int soc_bind_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) +/** + * snd_soc_add_dai_link - Add a DAI link dynamically + * @card: The ASoC card to which the DAI link is added + * @dai_link: The new DAI link to add + * + * This function adds a DAI link to the ASoC card's link list. + * + * Note: Topology can use this API to add DAI links when probing the + * topology component. And machine drivers can still define static + * DAI links in dai_link array. + */ +int snd_soc_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link_component *codec, *platform; struct snd_soc_component *component; - int i; + int i, ret; + + lockdep_assert_held(&client_mutex); + + /* + * Notify the machine driver for extra initialization + */ + if (card->add_dai_link) + card->add_dai_link(card, dai_link); if (dai_link->ignore) return 0; dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); - if (soc_is_dai_link_bound(card, dai_link)) { - dev_dbg(card->dev, "ASoC: dai link %s already bound\n", - dai_link->name); - return 0; - } + ret = soc_dai_link_sanity_check(card, dai_link); + if (ret < 0) + return ret; rtd = soc_new_pcm_runtime(card, dai_link); if (!rtd) @@ -930,13 +1158,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card, } } - soc_add_pcm_runtime(card, rtd); + /* see for_each_card_links */ + list_add_tail(&dai_link->list, &card->dai_link_list); + return 0; _err_defer: soc_free_pcm_runtime(rtd); return -EPROBE_DEFER; } +EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); static void soc_set_of_name_prefix(struct snd_soc_component *component) { @@ -973,8 +1204,16 @@ static void soc_set_name_prefix(struct snd_soc_card *card, soc_set_of_name_prefix(component); } -static void soc_cleanup_component(struct snd_soc_component *component) +static void soc_remove_component(struct snd_soc_component *component, + int probed) { + + if (!component->card) + return; + + if (probed) + snd_soc_component_remove(component); + /* For framework level robustness */ snd_soc_component_set_jack(component, NULL, NULL); @@ -985,22 +1224,13 @@ static void soc_cleanup_component(struct snd_soc_component *component) snd_soc_component_module_put_when_remove(component); } -static void soc_remove_component(struct snd_soc_component *component) -{ - if (!component->card) - return; - - snd_soc_component_remove(component); - - soc_cleanup_component(component); -} - static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; + int probed = 0; int ret; if (!strcmp(component->name, "snd-soc-dummy")) @@ -1056,6 +1286,7 @@ static int soc_probe_component(struct snd_soc_card *card, dapm->bias_level != SND_SOC_BIAS_OFF, "codec %s can not start from non-off bias with idle_bias_off==1\n", component->name); + probed = 1; /* machine specific init */ if (component->init) { @@ -1084,7 +1315,7 @@ static int soc_probe_component(struct snd_soc_card *card, err_probe: if (ret < 0) - soc_cleanup_component(component); + soc_remove_component(component, probed); return ret; } @@ -1126,7 +1357,6 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order) return 0; } -static void soc_rtd_free(struct snd_soc_pcm_runtime *rtd); /* remove me */ static void soc_remove_link_dais(struct snd_soc_card *card) { int i; @@ -1136,10 +1366,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card) for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - - /* finalize rtd device */ - soc_rtd_free(rtd); - /* remove the CODEC DAI */ for_each_rtd_codec_dai(rtd, i, codec_dai) soc_remove_dai(codec_dai, order); @@ -1187,13 +1413,11 @@ static void soc_remove_link_components(struct snd_soc_card *card) for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (component->driver->remove_order != order) continue; - soc_remove_component(component); + soc_remove_component(component, 1); } } } @@ -1208,9 +1432,7 @@ static int soc_probe_link_components(struct snd_soc_card *card) for_each_comp_order(order) { for_each_card_rtds(card, rtd) { - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (component->driver->probe_order != order) continue; @@ -1224,119 +1446,6 @@ static int soc_probe_link_components(struct snd_soc_card *card) return 0; } -static void soc_remove_dai_links(struct snd_soc_card *card) -{ - struct snd_soc_dai_link *link, *_link; - - soc_remove_link_dais(card); - - soc_remove_link_components(card); - - for_each_card_links_safe(card, link, _link) { - if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK) - dev_warn(card->dev, "Topology forgot to remove link %s?\n", - link->name); - - list_del(&link->list); - } -} - -static int soc_init_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *link) -{ - int i; - struct snd_soc_dai_link_component *codec, *platform; - - for_each_link_codecs(link, i, codec) { - /* - * Codec must be specified by 1 of name or OF node, - * not both or neither. - */ - if (!!codec->name == !!codec->of_node) { - dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - - /* Codec DAI name must be specified */ - if (!codec->dai_name) { - dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", - link->name); - return -EINVAL; - } - - /* - * Defer card registration if codec component is not added to - * component list. - */ - if (!soc_find_component(codec)) - return -EPROBE_DEFER; - } - - for_each_link_platforms(link, i, platform) { - /* - * Platform may be specified by either name or OF node, but it - * can be left unspecified, then no components will be inserted - * in the rtdcom list - */ - if (!!platform->name == !!platform->of_node) { - dev_err(card->dev, - "ASoC: Neither/both platform name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - - /* - * Defer card registration if platform component is not added to - * component list. - */ - if (!soc_find_component(platform)) - return -EPROBE_DEFER; - } - - /* FIXME */ - if (link->num_cpus > 1) { - dev_err(card->dev, - "ASoC: multi cpu is not yet supported %s\n", - link->name); - return -EINVAL; - } - - /* - * CPU device may be specified by either name or OF node, but - * can be left unspecified, and will be matched based on DAI - * name alone.. - */ - if (link->cpus->name && link->cpus->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - - /* - * Defer card registartion if cpu dai component is not added to - * component list. - */ - if ((link->cpus->of_node || link->cpus->name) && - !soc_find_component(link->cpus)) - return -EPROBE_DEFER; - - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!link->cpus->dai_name && - !(link->cpus->name || link->cpus->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - - return 0; -} - void snd_soc_disconnect_sync(struct device *dev) { struct snd_soc_component *component = @@ -1349,117 +1458,6 @@ void snd_soc_disconnect_sync(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); -/** - * snd_soc_add_dai_link - Add a DAI link dynamically - * @card: The ASoC card to which the DAI link is added - * @dai_link: The new DAI link to add - * - * This function adds a DAI link to the ASoC card's link list. - * - * Note: Topology can use this API to add DAI links when probing the - * topology component. And machine drivers can still define static - * DAI links in dai_link array. - */ -int snd_soc_add_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) -{ - if (dai_link->dobj.type - && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { - dev_err(card->dev, "Invalid dai link type %d\n", - dai_link->dobj.type); - return -EINVAL; - } - - lockdep_assert_held(&client_mutex); - /* - * Notify the machine driver for extra initialization - * on the link created by topology. - */ - if (dai_link->dobj.type && card->add_dai_link) - card->add_dai_link(card, dai_link); - - /* see for_each_card_links */ - list_add_tail(&dai_link->list, &card->dai_link_list); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); - -/** - * snd_soc_remove_dai_link - Remove a DAI link from the list - * @card: The ASoC card that owns the link - * @dai_link: The DAI link to remove - * - * This function removes a DAI link from the ASoC card's link list. - * - * For DAI links previously added by topology, topology should - * remove them by using the dobj embedded in the link. - */ -void snd_soc_remove_dai_link(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) -{ - if (dai_link->dobj.type - && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { - dev_err(card->dev, "Invalid dai link type %d\n", - dai_link->dobj.type); - return; - } - - lockdep_assert_held(&client_mutex); - /* - * Notify the machine driver for extra destruction - * on the link created by topology. - */ - if (dai_link->dobj.type && card->remove_dai_link) - card->remove_dai_link(card, dai_link); - - list_del(&dai_link->list); -} -EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); - -static void soc_rtd_free(struct snd_soc_pcm_runtime *rtd) -{ - if (rtd->dev_registered) { - /* we don't need to call kfree() for rtd->dev */ - device_unregister(rtd->dev); - rtd->dev_registered = 0; - } -} - -static void soc_rtd_release(struct device *dev) -{ - kfree(dev); -} - -static int soc_rtd_init(struct snd_soc_pcm_runtime *rtd, const char *name) -{ - int ret = 0; - - /* register the rtd device */ - rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!rtd->dev) - return -ENOMEM; - rtd->dev->parent = rtd->card->dev; - rtd->dev->release = soc_rtd_release; - rtd->dev->groups = soc_dev_attr_groups; - dev_set_name(rtd->dev, "%s", name); - dev_set_drvdata(rtd->dev, rtd); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); - INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); - ret = device_register(rtd->dev); - if (ret < 0) { - /* calling put_device() here to free the rtd->dev */ - put_device(rtd->dev); - dev_err(rtd->card->dev, - "ASoC: failed to register runtime device: %d\n", ret); - return ret; - } - rtd->dev_registered = 1; - return 0; -} - static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, struct snd_soc_pcm_runtime *rtd) { @@ -1509,10 +1507,6 @@ static int soc_link_init(struct snd_soc_card *card, return ret; } - ret = soc_rtd_init(rtd, dai_link->name); - if (ret) - return ret; - /* add DPCM sysfs entries */ soc_dpcm_debugfs_add(rtd); @@ -1523,9 +1517,7 @@ static int soc_link_init(struct snd_soc_card *card, * topology based drivers can use the DAI link id field to set PCM * device number and then use rtd + a base offset of the BEs. */ - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (!component->driver->use_dai_pcm_id) continue; @@ -1590,21 +1582,18 @@ static int soc_bind_aux_dev(struct snd_soc_card *card) static int soc_probe_aux_devices(struct snd_soc_card *card) { - struct snd_soc_component *comp; + struct snd_soc_component *component; int order; int ret; for_each_comp_order(order) { - for_each_card_auxs(card, comp) { - if (comp->driver->probe_order == order) { - ret = soc_probe_component(card, comp); - if (ret < 0) { - dev_err(card->dev, - "ASoC: failed to probe aux component %s %d\n", - comp->name, ret); - return ret; - } - } + for_each_card_auxs(card, component) { + if (component->driver->probe_order != order) + continue; + + ret = soc_probe_component(card, component); + if (ret < 0) + return ret; } } @@ -1619,7 +1608,7 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) for_each_comp_order(order) { for_each_card_auxs_safe(card, comp, _comp) { if (comp->driver->remove_order == order) - soc_remove_component(comp); + soc_remove_component(comp, 1); } } } @@ -1729,6 +1718,23 @@ static int is_dmi_valid(const char *field) return 1; } +/* + * Append a string to card->dmi_longname with character cleanups. + */ +static void append_dmi_string(struct snd_soc_card *card, const char *str) +{ + char *dst = card->dmi_longname; + size_t dst_len = sizeof(card->dmi_longname); + size_t len; + + len = strlen(dst); + snprintf(dst + len, dst_len - len, "-%s", str); + + len++; /* skip the separator "-" */ + if (len < dst_len) + cleanup_dmi_name(dst + len); +} + /** * snd_soc_set_dmi_name() - Register DMI names to card * @card: The card to register DMI names @@ -1763,61 +1769,37 @@ static int is_dmi_valid(const char *field) int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) { const char *vendor, *product, *product_version, *board; - size_t longname_buf_size = sizeof(card->snd_card->longname); - size_t len; if (card->long_name) return 0; /* long name already set by driver or from DMI */ - /* make up dmi long name as: vendor.product.version.board */ + /* make up dmi long name as: vendor-product-version-board */ vendor = dmi_get_system_info(DMI_BOARD_VENDOR); if (!vendor || !is_dmi_valid(vendor)) { dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); return 0; } - snprintf(card->dmi_longname, sizeof(card->snd_card->longname), - "%s", vendor); + snprintf(card->dmi_longname, sizeof(card->dmi_longname), "%s", vendor); cleanup_dmi_name(card->dmi_longname); product = dmi_get_system_info(DMI_PRODUCT_NAME); if (product && is_dmi_valid(product)) { - len = strlen(card->dmi_longname); - snprintf(card->dmi_longname + len, - longname_buf_size - len, - "-%s", product); - - len++; /* skip the separator "-" */ - if (len < longname_buf_size) - cleanup_dmi_name(card->dmi_longname + len); + append_dmi_string(card, product); /* * some vendors like Lenovo may only put a self-explanatory * name in the product version field */ product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); - if (product_version && is_dmi_valid(product_version)) { - len = strlen(card->dmi_longname); - snprintf(card->dmi_longname + len, - longname_buf_size - len, - "-%s", product_version); - - len++; - if (len < longname_buf_size) - cleanup_dmi_name(card->dmi_longname + len); - } + if (product_version && is_dmi_valid(product_version)) + append_dmi_string(card, product_version); } board = dmi_get_system_info(DMI_BOARD_NAME); if (board && is_dmi_valid(board)) { - len = strlen(card->dmi_longname); - snprintf(card->dmi_longname + len, - longname_buf_size - len, - "-%s", board); - - len++; - if (len < longname_buf_size) - cleanup_dmi_name(card->dmi_longname + len); + if (!product || strcasecmp(board, product)) + append_dmi_string(card, board); } else if (!product) { /* fall back to using legacy name */ dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); @@ -1825,16 +1807,8 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) } /* Add flavour to dmi long name */ - if (flavour) { - len = strlen(card->dmi_longname); - snprintf(card->dmi_longname + len, - longname_buf_size - len, - "-%s", flavour); - - len++; - if (len < longname_buf_size) - cleanup_dmi_name(card->dmi_longname + len); - } + if (flavour) + append_dmi_string(card, flavour); /* set the card long name */ card->long_name = card->dmi_longname; @@ -1853,7 +1827,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) for_each_component(component) { - /* does this component override FEs ? */ + /* does this component override BEs ? */ if (!component->driver->ignore_machine) continue; @@ -1874,7 +1848,7 @@ match: continue; } - dev_info(card->dev, "info: override FE DAI link %s\n", + dev_info(card->dev, "info: override BE DAI link %s\n", card->dai_link[i].name); /* override platform component */ @@ -1918,17 +1892,58 @@ match: } } -static void soc_cleanup_card_resources(struct snd_soc_card *card) +#define soc_setup_card_name(name, name1, name2, norm) \ + __soc_setup_card_name(name, sizeof(name), name1, name2, norm) +static void __soc_setup_card_name(char *name, int len, + const char *name1, const char *name2, + int normalization) { - /* free the ALSA card at first; this syncs with pending operations */ - if (card->snd_card) { - snd_card_free(card->snd_card); - card->snd_card = NULL; + int i; + + snprintf(name, len, "%s", name1 ? name1 : name2); + + if (!normalization) + return; + + /* + * Name normalization + * + * The driver name is somewhat special, as it's used as a key for + * searches in the user-space. + * + * ex) + * "abcd??efg" -> "abcd__efg" + */ + for (i = 0; i < len; i++) { + switch (name[i]) { + case '_': + case '-': + case '\0': + break; + default: + if (!isalnum(name[i])) + name[i] = '_'; + break; + } } +} + +static void soc_cleanup_card_resources(struct snd_soc_card *card, + int card_probed) +{ + struct snd_soc_dai_link *link, *_link; + + if (card->snd_card) + snd_card_disconnect_sync(card->snd_card); + + snd_soc_dapm_shutdown(card); /* remove and free each DAI */ - soc_remove_dai_links(card); - soc_remove_pcm_runtimes(card); + soc_remove_link_dais(card); + soc_remove_link_components(card); + + for_each_card_links_safe(card, link, _link) + snd_soc_remove_dai_link(card, link); /* remove auxiliary devices */ soc_remove_aux_devices(card); @@ -1938,26 +1953,39 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card) soc_cleanup_card_debugfs(card); /* remove the card */ - if (card->remove) + if (card_probed && card->remove) card->remove(card); + + if (card->snd_card) { + snd_card_free(card->snd_card); + card->snd_card = NULL; + } +} + +static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) +{ + if (card->instantiated) { + int card_probed = 1; + + card->instantiated = false; + snd_soc_flush_all_delayed_work(card); + + soc_cleanup_card_resources(card, card_probed); + if (!unregister) + list_add(&card->list, &unbind_card_list); + } else { + if (unregister) + list_del(&card->list); + } } -static int snd_soc_instantiate_card(struct snd_soc_card *card) +static int snd_soc_bind_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link *dai_link; - int ret, i; + int ret, i, card_probed = 0; mutex_lock(&client_mutex); - for_each_card_prelinks(card, i, dai_link) { - ret = soc_init_dai_link(card, dai_link); - if (ret) { - dev_err(card->dev, "ASoC: failed to init link %s: %d\n", - dai_link->name, ret); - mutex_unlock(&client_mutex); - return ret; - } - } mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); snd_soc_dapm_init(&card->dapm, card, NULL); @@ -1965,19 +1993,13 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* check whether any platform is ignore machine FE and using topology */ soc_check_tplg_fes(card); - /* bind DAIs */ - for_each_card_prelinks(card, i, dai_link) { - ret = soc_bind_dai_link(card, dai_link); - if (ret != 0) - goto probe_end; - } - /* bind aux_devs too */ ret = soc_bind_aux_dev(card); if (ret < 0) goto probe_end; /* add predefined DAI links to the list */ + card->num_rtd = 0; for_each_card_prelinks(card, i, dai_link) { ret = snd_soc_add_dai_link(card, dai_link); if (ret < 0) @@ -2013,6 +2035,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) ret = card->probe(card); if (ret < 0) goto probe_end; + card_probed = 1; } /* probe all components used by DAI links on this card */ @@ -2025,23 +2048,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* probe auxiliary components */ ret = soc_probe_aux_devices(card); - if (ret < 0) + if (ret < 0) { + dev_err(card->dev, + "ASoC: failed to probe aux component %d\n", ret); goto probe_end; - - /* - * Find new DAI links added during probing components and bind them. - * Components with topology may bring new DAIs and DAI links. - */ - for_each_card_links(card, dai_link) { - if (soc_is_dai_link_bound(card, dai_link)) - continue; - - ret = soc_init_dai_link(card, dai_link); - if (ret) - goto probe_end; - ret = soc_bind_dai_link(card, dai_link); - if (ret) - goto probe_end; } /* probe all DAI links on this card */ @@ -2076,22 +2086,23 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* try to set some sane longname if DMI is available */ snd_soc_set_dmi_name(card, NULL); - snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), - "%s", card->name); - snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), - "%s", card->long_name ? card->long_name : card->name); - snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), - "%s", card->driver_name ? card->driver_name : card->name); - for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { - switch (card->snd_card->driver[i]) { - case '_': - case '-': - case '\0': - break; - default: - if (!isalnum(card->snd_card->driver[i])) - card->snd_card->driver[i] = '_'; - break; + soc_setup_card_name(card->snd_card->shortname, + card->name, NULL, 0); + soc_setup_card_name(card->snd_card->longname, + card->long_name, card->name, 0); + soc_setup_card_name(card->snd_card->driver, + card->driver_name, card->name, 1); + + if (card->components) { + /* the current implementation of snd_component_add() accepts */ + /* multiple components in the string separated by space, */ + /* but the string collision (identical string) check might */ + /* not work correctly */ + ret = snd_component_add(card->snd_card, card->components); + if (ret < 0) { + dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n", + card->name, ret); + goto probe_end; } } @@ -2103,6 +2114,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto probe_end; } } + card_probed = 1; snd_soc_dapm_new_widgets(card); @@ -2117,9 +2129,22 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); + /* deactivate pins to sleep state */ + for_each_card_rtds(card, rtd) { + struct snd_soc_dai *dai; + + for_each_rtd_codec_dai(rtd, i, dai) { + if (!dai->active) + pinctrl_pm_select_sleep_state(dai->dev); + } + + if (!rtd->cpu_dai->active) + pinctrl_pm_select_sleep_state(rtd->cpu_dai->dev); + } + probe_end: if (ret < 0) - soc_cleanup_card_resources(card); + soc_cleanup_card_resources(card, card_probed); mutex_unlock(&card->mutex); mutex_unlock(&client_mutex); @@ -2349,33 +2374,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); -static int snd_soc_bind_card(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - int ret; - - ret = snd_soc_instantiate_card(card); - if (ret != 0) - return ret; - - /* deactivate pins to sleep state */ - for_each_card_rtds(card, rtd) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai; - int j; - - for_each_rtd_codec_dai(rtd, j, codec_dai) { - if (!codec_dai->active) - pinctrl_pm_select_sleep_state(codec_dai->dev); - } - - if (!cpu_dai->active) - pinctrl_pm_select_sleep_state(cpu_dai->dev); - } - - return ret; -} - /** * snd_soc_register_card - Register a card with the ASoC core * @@ -2400,7 +2398,6 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->dapm_dirty); INIT_LIST_HEAD(&card->dobj_list); - card->num_rtd = 0; card->instantiated = 0; mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); @@ -2411,25 +2408,6 @@ int snd_soc_register_card(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(snd_soc_register_card); -static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) -{ - if (card->instantiated) { - card->instantiated = false; - snd_soc_dapm_shutdown(card); - snd_soc_flush_all_delayed_work(card); - - /* remove all components used by DAI links on this card */ - soc_remove_link_components(card); - - soc_cleanup_card_resources(card); - if (!unregister) - list_add(&card->list, &unbind_card_list); - } else { - if (unregister) - list_del(&card->list); - } -} - /** * snd_soc_unregister_card - Unregister a card with the ASoC core * @@ -2488,7 +2466,7 @@ static char *fmt_single_name(struct device *dev, int *id) *id = 0; } - return kstrdup(name, GFP_KERNEL); + return devm_kstrdup(dev, name, GFP_KERNEL); } /* @@ -2505,38 +2483,38 @@ static inline char *fmt_multiple_name(struct device *dev, return NULL; } - return kstrdup(dai_drv->name, GFP_KERNEL); + return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL); } -/** - * snd_soc_unregister_dai - Unregister DAIs from the ASoC core - * - * @component: The component for which the DAIs should be unregistered - */ -static void snd_soc_unregister_dais(struct snd_soc_component *component) +void snd_soc_unregister_dai(struct snd_soc_dai *dai) { - struct snd_soc_dai *dai, *_dai; - - for_each_component_dais_safe(component, dai, _dai) { - dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", - dai->name); - list_del(&dai->list); - kfree(dai->name); - kfree(dai); - } + dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); + list_del(&dai->list); } +EXPORT_SYMBOL_GPL(snd_soc_unregister_dai); -/* Create a DAI and add it to the component's DAI list */ -static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, - bool legacy_dai_naming) +/** + * snd_soc_register_dai - Register a DAI dynamically & create its widgets + * + * @component: The component the DAIs are registered for + * @dai_drv: DAI driver to use for the DAI + * + * Topology can use this API to register DAIs when probing a component. + * These DAIs's widgets will be freed in the card cleanup and the DAIs + * will be freed in the component cleanup. + */ +struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, + struct snd_soc_dai_driver *dai_drv, + bool legacy_dai_naming) { struct device *dev = component->dev; struct snd_soc_dai *dai; dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); - dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); + lockdep_assert_held(&client_mutex); + + dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL); if (dai == NULL) return NULL; @@ -2558,10 +2536,8 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, else dai->id = component->num_dai; } - if (dai->name == NULL) { - kfree(dai); + if (!dai->name) return NULL; - } dai->component = component; dai->dev = dev; @@ -2578,6 +2554,19 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, } /** + * snd_soc_unregister_dai - Unregister DAIs from the ASoC core + * + * @component: The component for which the DAIs should be unregistered + */ +static void snd_soc_unregister_dais(struct snd_soc_component *component) +{ + struct snd_soc_dai *dai, *_dai; + + for_each_component_dais_safe(component, dai, _dai) + snd_soc_unregister_dai(dai); +} + +/** * snd_soc_register_dais - Register a DAI with the ASoC core * * @component: The component the DAIs are registered for @@ -2588,16 +2577,12 @@ static int snd_soc_register_dais(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, size_t count) { - struct device *dev = component->dev; struct snd_soc_dai *dai; unsigned int i; int ret; - dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count); - for (i = 0; i < count; i++) { - - dai = soc_add_dai(component, dai_drv + i, count == 1 && + dai = snd_soc_register_dai(component, dai_drv + i, count == 1 && !component->driver->non_legacy_dai_naming); if (dai == NULL) { ret = -ENOMEM; @@ -2613,49 +2598,6 @@ err: return ret; } -/** - * snd_soc_register_dai - Register a DAI dynamically & create its widgets - * - * @component: The component the DAIs are registered for - * @dai_drv: DAI driver to use for the DAI - * - * Topology can use this API to register DAIs when probing a component. - * These DAIs's widgets will be freed in the card cleanup and the DAIs - * will be freed in the component cleanup. - */ -int snd_soc_register_dai(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv) -{ - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); - struct snd_soc_dai *dai; - int ret; - - if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) { - dev_err(component->dev, "Invalid dai type %d\n", - dai_drv->dobj.type); - return -EINVAL; - } - - lockdep_assert_held(&client_mutex); - dai = soc_add_dai(component, dai_drv, false); - if (!dai) - return -ENOMEM; - - /* - * Create the DAI widgets here. After adding DAIs, topology may - * also add routes that need these widgets as source or sink. - */ - ret = snd_soc_dapm_new_dai_widgets(dapm, dai); - if (ret != 0) { - dev_err(component->dev, - "Failed to create DAI widgets %d\n", ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_register_dai); - static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -2726,40 +2668,6 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); #endif -static void snd_soc_component_add(struct snd_soc_component *component) -{ - mutex_lock(&client_mutex); - - if (!component->driver->write && !component->driver->read) { - if (!component->regmap) - component->regmap = dev_get_regmap(component->dev, - NULL); - if (component->regmap) - snd_soc_component_setup_regmap(component); - } - - /* see for_each_component */ - list_add(&component->list, &component_list); - - mutex_unlock(&client_mutex); -} - -static void snd_soc_component_cleanup(struct snd_soc_component *component) -{ - snd_soc_unregister_dais(component); - kfree(component->name); -} - -static void snd_soc_component_del_unlocked(struct snd_soc_component *component) -{ - struct snd_soc_card *card = component->card; - - if (card) - snd_soc_unbind_card(card, false); - - list_del(&component->list); -} - #define ENDIANNESS_MAP(name) \ (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) static u64 endianness_format_map[] = { @@ -2804,6 +2712,18 @@ static void snd_soc_try_rebind_card(void) list_del(&card->list); } +static void snd_soc_del_component_unlocked(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + + snd_soc_unregister_dais(component); + + if (card) + snd_soc_unbind_card(card, false); + + list_del(&component->list); +} + int snd_soc_add_component(struct device *dev, struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, @@ -2813,6 +2733,8 @@ int snd_soc_add_component(struct device *dev, int ret; int i; + mutex_lock(&client_mutex); + ret = snd_soc_component_initialize(component, component_driver, dev); if (ret) goto err_free; @@ -2830,14 +2752,26 @@ int snd_soc_add_component(struct device *dev, goto err_cleanup; } - snd_soc_component_add(component); - snd_soc_try_rebind_card(); + if (!component->driver->write && !component->driver->read) { + if (!component->regmap) + component->regmap = dev_get_regmap(component->dev, + NULL); + if (component->regmap) + snd_soc_component_setup_regmap(component); + } - return 0; + /* see for_each_component */ + list_add(&component->list, &component_list); err_cleanup: - snd_soc_component_cleanup(component); + if (ret < 0) + snd_soc_del_component_unlocked(component); err_free: + mutex_unlock(&client_mutex); + + if (ret == 0) + snd_soc_try_rebind_card(); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_add_component); @@ -2864,62 +2798,21 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component); * * @dev: The device to unregister */ -static int __snd_soc_unregister_component(struct device *dev) -{ - struct snd_soc_component *component; - int found = 0; - - mutex_lock(&client_mutex); - for_each_component(component) { - if (dev != component->dev) - continue; - - snd_soc_tplg_component_remove(component, - SND_SOC_TPLG_INDEX_ALL); - snd_soc_component_del_unlocked(component); - found = 1; - break; - } - mutex_unlock(&client_mutex); - - if (found) - snd_soc_component_cleanup(component); - - return found; -} - void snd_soc_unregister_component(struct device *dev) { - while (__snd_soc_unregister_component(dev)) - ; -} -EXPORT_SYMBOL_GPL(snd_soc_unregister_component); - -struct snd_soc_component *snd_soc_lookup_component(struct device *dev, - const char *driver_name) -{ struct snd_soc_component *component; - struct snd_soc_component *ret; - ret = NULL; mutex_lock(&client_mutex); - for_each_component(component) { - if (dev != component->dev) - continue; - - if (driver_name && - (driver_name != component->driver->name) && - (strcmp(component->driver->name, driver_name) != 0)) - continue; + while (1) { + component = snd_soc_lookup_component_nolocked(dev, NULL); + if (!component) + break; - ret = component; - break; + snd_soc_del_component_unlocked(component); } mutex_unlock(&client_mutex); - - return ret; } -EXPORT_SYMBOL_GPL(snd_soc_lookup_component); +EXPORT_SYMBOL_GPL(snd_soc_unregister_component); /* Retrieve a card's name from device tree */ int snd_soc_of_parse_card_name(struct snd_soc_card *card, diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 5552c66ca642..a428ff393ea2 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -75,12 +75,10 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config); -static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int dmaengine_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); int (*prepare_slave_config)(struct snd_pcm_substream *substream, @@ -109,21 +107,16 @@ static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream) +static int +dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct device *dma_dev = dmaengine_dma_dev(pcm, substream); struct dma_chan *chan = pcm->chan[substream->stream]; struct snd_dmaengine_dai_dma_data *dma_data; - struct dma_slave_caps dma_caps; struct snd_pcm_hardware hw; - u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | - BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | - BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); - snd_pcm_format_t i; int ret; if (pcm->config && pcm->config->pcm_hardware) @@ -145,82 +138,53 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) hw.info |= SNDRV_PCM_INFO_BATCH; - ret = dma_get_slave_caps(chan, &dma_caps); - if (ret == 0) { - if (dma_caps.cmd_pause && dma_caps.cmd_resume) - hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; - if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) - hw.info |= SNDRV_PCM_INFO_BATCH; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - addr_widths = dma_caps.dst_addr_widths; - else - addr_widths = dma_caps.src_addr_widths; - } - - /* - * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep - * hw.formats set to 0, meaning no restrictions are in place. - * In this case it's the responsibility of the DAI driver to - * provide the supported format information. - */ - if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) - /* - * Prepare formats mask for valid/allowed sample types. If the - * dma does not have support for the given physical word size, - * it needs to be masked out so user space can not use the - * format which produces corrupted audio. - * In case the dma driver does not implement the slave_caps the - * default assumption is that it supports 1, 2 and 4 bytes - * widths. - */ - for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) { - int bits = snd_pcm_format_physical_width(i); - - /* - * Enable only samples with DMA supported physical - * widths - */ - switch (bits) { - case 8: - case 16: - case 24: - case 32: - case 64: - if (addr_widths & (1 << (bits / 8))) - hw.formats |= pcm_format_to_bits(i); - break; - default: - /* Unsupported types */ - break; - } - } + ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream, + dma_data, + &hw, + chan); + if (ret) + return ret; return snd_soc_set_runtime_hwparams(substream, &hw); } -static int dmaengine_pcm_open(struct snd_pcm_substream *substream) +static int dmaengine_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct dma_chan *chan = pcm->chan[substream->stream]; int ret; - ret = dmaengine_pcm_set_runtime_hwparams(substream); + ret = dmaengine_pcm_set_runtime_hwparams(component, substream); if (ret) return ret; return snd_dmaengine_pcm_open(substream, chan); } +static int dmaengine_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_close(substream); +} + +static int dmaengine_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int dmaengine_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + return snd_dmaengine_pcm_trigger(substream, cmd); +} + static struct dma_chan *dmaengine_pcm_compat_request_channel( + struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct snd_dmaengine_dai_dma_data *dma_data; dma_filter_fn fn = NULL; @@ -258,10 +222,9 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, return true; } -static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int dmaengine_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); const struct snd_dmaengine_pcm_config *config = pcm->config; struct device *dev = component->dev; @@ -288,8 +251,8 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) config->chan_names[i]); if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) { - pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, - substream); + pcm->chan[i] = dmaengine_pcm_compat_request_channel( + component, rtd, substream); } if (!pcm->chan[i]) { @@ -318,11 +281,9 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) } static snd_pcm_uframes_t dmaengine_pcm_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct dmaengine_pcm *pcm = soc_component_to_pcm(component); if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) @@ -331,13 +292,11 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( return snd_dmaengine_pcm_pointer(substream); } -static int dmaengine_copy_user(struct snd_pcm_substream *substream, +static int dmaengine_copy_user(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channel, unsigned long hwoff, void __user *buf, unsigned long bytes) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SND_DMAENGINE_PCM_DRV_NAME); struct snd_pcm_runtime *runtime = substream->runtime; struct dmaengine_pcm *pcm = soc_component_to_pcm(component); int (*process)(struct snd_pcm_substream *substream, @@ -365,39 +324,31 @@ static int dmaengine_copy_user(struct snd_pcm_substream *substream, return 0; } -static const struct snd_pcm_ops dmaengine_pcm_ops = { +static const struct snd_soc_component_driver dmaengine_pcm_component = { + .name = SND_DMAENGINE_PCM_DRV_NAME, + .probe_order = SND_SOC_COMP_ORDER_LATE, .open = dmaengine_pcm_open, - .close = snd_dmaengine_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .close = dmaengine_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = dmaengine_pcm_hw_params, - .hw_free = snd_pcm_lib_free_pages, - .trigger = snd_dmaengine_pcm_trigger, + .hw_free = dmaengine_pcm_hw_free, + .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, + .pcm_construct = dmaengine_pcm_new, }; -static const struct snd_pcm_ops dmaengine_pcm_process_ops = { +static const struct snd_soc_component_driver dmaengine_pcm_component_process = { + .name = SND_DMAENGINE_PCM_DRV_NAME, + .probe_order = SND_SOC_COMP_ORDER_LATE, .open = dmaengine_pcm_open, - .close = snd_dmaengine_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .close = dmaengine_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = dmaengine_pcm_hw_params, - .hw_free = snd_pcm_lib_free_pages, - .trigger = snd_dmaengine_pcm_trigger, + .hw_free = dmaengine_pcm_hw_free, + .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .copy_user = dmaengine_copy_user, -}; - -static const struct snd_soc_component_driver dmaengine_pcm_component = { - .name = SND_DMAENGINE_PCM_DRV_NAME, - .probe_order = SND_SOC_COMP_ORDER_LATE, - .ops = &dmaengine_pcm_ops, - .pcm_new = dmaengine_pcm_new, -}; - -static const struct snd_soc_component_driver dmaengine_pcm_component_process = { - .name = SND_DMAENGINE_PCM_DRV_NAME, - .probe_order = SND_SOC_COMP_ORDER_LATE, - .ops = &dmaengine_pcm_process_ops, - .pcm_new = dmaengine_pcm_new, + .pcm_construct = dmaengine_pcm_new, }; static const char * const dmaengine_pcm_dma_channel_names[] = { @@ -436,7 +387,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, name = dmaengine_pcm_dma_channel_names[i]; if (config && config->chan_names[i]) name = config->chan_names[i]; - chan = dma_request_slave_channel_reason(dev, name); + chan = dma_request_chan(dev, name); if (IS_ERR(chan)) { if (PTR_ERR(chan) == -EPROBE_DEFER) return -EPROBE_DEFER; diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index f4dc3d445aae..652657dc6809 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -592,23 +592,16 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { - struct snd_card *snd_card = card->snd_card; struct snd_kcontrol *kctl; struct soc_mixer_control *mc; - int found = 0; int ret = -EINVAL; /* Sanity check for name and max */ if (unlikely(!name || max <= 0)) return -EINVAL; - list_for_each_entry(kctl, &snd_card->controls, list) { - if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) { - found = 1; - break; - } - } - if (found) { + kctl = snd_soc_card_get_kcontrol(card, name); + if (kctl) { mc = (struct soc_mixer_control *)kctl->private_value; if (max <= mc->max) { mc->platform_max = max; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b600d3eaaf5c..01eb8700c3de 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -118,11 +118,8 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) ignore &= !component->driver->use_pmdown_time; - } return ignore; } @@ -435,8 +432,7 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream, struct snd_soc_component *component; int ret = 0; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; + for_each_rtd_components(rtd, rtdcom, component) { *last = component; ret = snd_soc_component_module_get_when_open(component); @@ -467,9 +463,7 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, struct snd_soc_component *component; int ret = 0; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (component == last) break; @@ -500,9 +494,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) for_each_rtd_codec_dai(rtd, i, codec_dai) pinctrl_pm_select_default_state(codec_dai->dev); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { pm_runtime_get_sync(component->dev); } @@ -625,9 +617,7 @@ component_err: out: mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } @@ -740,9 +730,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } @@ -782,9 +770,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { ret = snd_soc_component_prepare(component, substream); if (ret < 0) { dev_err(component->dev, @@ -849,9 +835,7 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream, struct snd_soc_component *component; int ret = 0; - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { if (component == last) break; @@ -877,6 +861,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, int i, ret = 0; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + + ret = soc_pcm_params_symmetry(substream, params); + if (ret) + goto out; + if (rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); if (ret < 0) { @@ -945,9 +934,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_dapm_update_dai(substream, params, cpu_dai); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - + for_each_rtd_components(rtd, rtdcom, component) { ret = snd_soc_component_hw_params(component, substream, params); if (ret < 0) { dev_err(component->dev, @@ -958,9 +945,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } component = NULL; - ret = soc_pcm_params_symmetry(substream, params); - if (ret) - goto component_err; out: mutex_unlock(&rtd->card->pcm_mutex); return ret; @@ -1047,7 +1031,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; @@ -1056,16 +1040,42 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_dai *codec_dai; int i, ret; + if (rtd->dai_link->ops->trigger) { + ret = rtd->dai_link->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + + for_each_rtd_components(rtd, rtdcom, component) { + ret = snd_soc_component_trigger(component, substream, cmd); + if (ret < 0) + return ret; + } + + ret = snd_soc_dai_trigger(cpu_dai, substream, cmd); + if (ret < 0) + return ret; + for_each_rtd_codec_dai(rtd, i, codec_dai) { ret = snd_soc_dai_trigger(codec_dai, substream, cmd); if (ret < 0) return ret; } - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; + return 0; +} - ret = snd_soc_component_trigger(component, substream, cmd); +static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai; + int i, ret; + + for_each_rtd_codec_dai(rtd, i, codec_dai) { + ret = snd_soc_dai_trigger(codec_dai, substream, cmd); if (ret < 0) return ret; } @@ -1074,6 +1084,12 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (ret < 0) return ret; + for_each_rtd_components(rtd, rtdcom, component) { + ret = snd_soc_component_trigger(component, substream, cmd); + if (ret < 0) + return ret; + } + if (rtd->dai_link->ops->trigger) { ret = rtd->dai_link->ops->trigger(substream, cmd); if (ret < 0) @@ -1083,6 +1099,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = soc_pcm_trigger_start(substream, cmd); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = soc_pcm_trigger_stop(substream, cmd); + break; + default: + return -EINVAL; + } + + return ret; +} + static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1146,7 +1184,9 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, { struct snd_soc_dpcm *dpcm; unsigned long flags; +#ifdef CONFIG_DEBUG_FS char *name; +#endif /* only add new dpcms */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1385,6 +1425,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget *widget; struct snd_soc_dai *dai; int prune = 0; + int do_prune; /* Destroy any old FE <--> BE connections */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1398,13 +1439,16 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, continue; /* is there a valid CODEC DAI widget for this BE */ + do_prune = 1; for_each_rtd_codec_dai(dpcm->be, i, dai) { widget = dai_get_widget(dai, stream); /* prune the BE if it's no longer in our active list */ if (widget && widget_in_list(list, widget)) - continue; + do_prune = 0; } + if (!do_prune) + continue; dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", stream ? "capture" : "playback", @@ -2289,42 +2333,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, } EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); +static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, + int cmd, bool fe_first) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int ret; + + /* call trigger on the frontend before the backend. */ + if (fe_first) { + dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + if (ret < 0) + return ret; + + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + return ret; + } + + /* call trigger on the frontend after the backend. */ + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + if (ret < 0) + return ret; + + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + + return ret; +} + static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *fe = substream->private_data; - int stream = substream->stream, ret; + int stream = substream->stream; + int ret = 0; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; switch (trigger) { case SND_SOC_DPCM_TRIGGER_PRE: - /* call trigger on the frontend before the backend. */ - - dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); - - ret = soc_pcm_trigger(substream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); + break; + default: + ret = -EINVAL; + break; } - - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); break; case SND_SOC_DPCM_TRIGGER_POST: - /* call trigger on the frontend after the backend. */ - - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); + break; + default: + ret = -EINVAL; + break; } - - dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); - - ret = soc_pcm_trigger(substream, cmd); break; case SND_SOC_DPCM_TRIGGER_BESPOKE: /* bespoke trigger() - handles both FE and BEs */ @@ -2333,10 +2416,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) fe->dai_link->name, cmd); ret = soc_pcm_bespoke_trigger(substream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; - } break; default: dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, @@ -2345,6 +2424,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) goto out; } + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", + cmd, ret); + goto out; + } + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -2809,21 +2894,13 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) return ret; } -static void soc_pcm_private_free(struct snd_pcm *pcm) -{ - struct snd_soc_pcm_runtime *rtd = pcm->private_data; - - /* need to sync the delayed work before releasing resources */ - flush_delayed_work(&rtd->delayed_work); - snd_soc_pcm_component_free(pcm); -} - /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; @@ -2935,17 +3012,14 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = snd_soc_pcm_component_ioctl; } - for_each_rtdcom(rtd, rtdcom) { - const struct snd_pcm_ops *ops = rtdcom->component->driver->ops; - - if (!ops) - continue; + for_each_rtd_components(rtd, rtdcom, component) { + const struct snd_soc_component_driver *drv = component->driver; - if (ops->copy_user) + if (drv->copy_user) rtd->ops.copy_user = snd_soc_pcm_component_copy_user; - if (ops->page) + if (drv->page) rtd->ops.page = snd_soc_pcm_component_page; - if (ops->mmap) + if (drv->mmap) rtd->ops.mmap = snd_soc_pcm_component_mmap; } @@ -2955,13 +3029,12 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); - ret = snd_soc_pcm_component_new(pcm); + ret = snd_soc_pcm_component_new(rtd); if (ret < 0) { dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret); return ret; } - pcm->private_free = soc_pcm_private_free; pcm->no_device_suspend = true; out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 0fd032914a31..81d2af000a5c 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1800,6 +1800,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_dai_driver *dai_drv; struct snd_soc_pcm_stream *stream; struct snd_soc_tplg_stream_caps *caps; + struct snd_soc_dai *dai; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(tplg->comp); int ret; dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); @@ -1842,7 +1845,19 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); /* register the DAI to the component */ - return snd_soc_register_dai(tplg->comp, dai_drv); + dai = snd_soc_register_dai(tplg->comp, dai_drv, false); + if (!dai) + return -ENOMEM; + + /* Create the DAI widgets here */ + ret = snd_soc_dapm_new_dai_widgets(dapm, dai); + if (ret != 0) { + dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret); + snd_soc_unregister_dai(dai); + return ret; + } + + return ret; } static void set_link_flags(struct snd_soc_dai_link *link, diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 54dcece52b0c..2fd4562f5e63 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -63,7 +63,8 @@ static const struct snd_pcm_hardware dummy_dma_hardware = { .periods_max = 128, }; -static int dummy_dma_open(struct snd_pcm_substream *substream) +static int dummy_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -74,13 +75,9 @@ static int dummy_dma_open(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops snd_dummy_dma_ops = { - .open = dummy_dma_open, - .ioctl = snd_pcm_lib_ioctl, -}; - static const struct snd_soc_component_driver dummy_platform = { - .ops = &snd_dummy_dma_ops, + .open = dummy_dma_open, + .ioctl = snd_soc_pcm_lib_ioctl, }; static const struct snd_soc_component_driver dummy_codec = { diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index bb8036ae567e..71a0fc075a63 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -14,8 +14,6 @@ config SND_SOC_SOF_PCI depends on PCI select SND_SOC_SOF select SND_SOC_ACPI if ACPI - select SND_SOC_SOF_OPTIONS - select SND_SOC_SOF_INTEL_PCI if SND_SOC_SOF_INTEL_TOPLEVEL help This adds support for PCI enumeration. This option is required to enable Intel Skylake+ devices @@ -27,8 +25,6 @@ config SND_SOC_SOF_ACPI depends on ACPI || COMPILE_TEST select SND_SOC_SOF select SND_SOC_ACPI if ACPI - select SND_SOC_SOF_OPTIONS - select SND_SOC_SOF_INTEL_ACPI if SND_SOC_SOF_INTEL_TOPLEVEL select IOSF_MBI if X86 && PCI help This adds support for ACPI enumeration. This option is required @@ -40,19 +36,23 @@ config SND_SOC_SOF_OF tristate "SOF OF enumeration support" depends on OF || COMPILE_TEST select SND_SOC_SOF - select SND_SOC_SOF_OPTIONS help This adds support for Device Tree enumeration. This option is required to enable i.MX8 devices. Say Y if you need this option. If unsure select "N". -config SND_SOC_SOF_OPTIONS - tristate +config SND_SOC_SOF_DEVELOPER_SUPPORT + bool "SOF developer options support" + depends on EXPERT help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level + This option unlock SOF developer options for debug/performance/ + code hardening. + Distributions should not select this option, only SOF development + teams should select it. + Say Y if you are involved in SOF development and need this option + If not, select N -if SND_SOC_SOF_OPTIONS +if SND_SOC_SOF_DEVELOPER_SUPPORT config SND_SOC_SOF_NOCODEC tristate @@ -64,6 +64,11 @@ config SND_SOC_SOF_NOCODEC_SUPPORT option if no known codec is detected. This is typically only enabled for developers or devices where the sound card is controlled externally + This option is mutually exclusive with the Intel HDaudio support, + selecting it may have negative impacts and prevent e.g. microphone + functionality from being enabled on Intel CoffeeLake and later + platforms. + Distributions should not select this option! Say Y if you need this nocodec fallback option If unsure select "N". @@ -142,6 +147,14 @@ config SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE Say Y if you want to enable caching the memory windows. If unsure, select "N". +config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE + bool "SOF enable firmware trace" + help + The firmware trace can be enabled either at build-time with + this option, or dynamically by setting flags in the SOF core + module parameter (similar to dynamic debug) + If unsure, select "N". + config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST bool "SOF enable IPC flood test" help @@ -150,9 +163,17 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT + bool "SOF retain DSP context on any FW exceptions" + help + This option keeps the DSP in D0 state so that firmware debug + information can be retained and dumped to userspace. + Say Y if you want to retain DSP context for FW exceptions. + If unsure, select "N". + endif ## SND_SOC_SOF_DEBUG -endif ## SND_SOC_SOF_OPTIONS +endif ## SND_SOC_SOF_DEVELOPER_SUPPORT config SND_SOC_SOF tristate diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 2b8711eda362..7baf7f1507c3 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -11,8 +11,39 @@ /* Mixer Controls */ #include <linux/pm_runtime.h> +#include <linux/leds.h> #include "sof-priv.h" +static void update_mute_led(struct snd_sof_control *scontrol, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int temp = 0; + unsigned int mask; + int i; + + mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + for (i = 0; i < scontrol->num_channels; i++) { + if (ucontrol->value.integer.value[i]) { + temp |= mask; + break; + } + } + + if (temp == scontrol->led_ctl.led_value) + return; + + scontrol->led_ctl.led_value = temp; + +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + if (!scontrol->led_ctl.direction) + ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON); + else + ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON); +#endif +} + static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) { if (value >= size) @@ -118,6 +149,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, cdata->chanv[i].value = value; } + if (scontrol->led_ctl.use_led) + update_mute_led(scontrol, kcontrol, ucontrol); + /* notify DSP of mixer updates */ if (pm_runtime_active(sdev->dev)) snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 81f28f7ff1a0..805918d3bcc0 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -16,6 +16,11 @@ #include "sof-priv.h" #include "ops.h" +/* see SOF_DBG_ flags */ +int sof_core_debug; +module_param_named(sof_debug, sof_core_debug, int, 0444); +MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); + /* SOF defaults if not provided by the platform in ms */ #define TIMEOUT_DEFAULT_IPC_MS 500 #define TIMEOUT_DEFAULT_BOOT_MS 2000 @@ -127,6 +132,19 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev, return NULL; } +bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev) +{ + struct snd_sof_pcm *spcm; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) + return true; + } + + return false; +} + /* * FW Panic/fault handling. */ @@ -350,12 +368,20 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto fw_run_err; } - /* init DMA trace */ - ret = snd_sof_init_trace(sdev); - if (ret < 0) { - /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to initialize trace %d\n", ret); + if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) || + (sof_core_debug & SOF_DBG_ENABLE_TRACE)) { + sdev->dtrace_is_supported = true; + + /* init DMA trace */ + ret = snd_sof_init_trace(sdev); + if (ret < 0) { + /* non fatal */ + dev_warn(sdev->dev, + "warning: failed to initialize trace %d\n", + ret); + } + } else { + dev_dbg(sdev->dev, "SOF firmware trace disabled\n"); } /* hereafter all FW boot flows are for PM reasons */ @@ -445,6 +471,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) /* initialize sof device */ sdev->dev = dev; + /* initialize default D0 sub-state */ + sdev->d0_substate = SOF_DSP_D0I0; + sdev->pdata = plat_data; sdev->first_boot = true; dev_set_drvdata(dev, sdev); @@ -453,7 +482,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run || !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware || - !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params) + !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params || + !sof_ops(sdev)->fw_ready) return -EINVAL; INIT_LIST_HEAD(&sdev->pcm_list); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 5529e8eeca46..d2b3b99d3a20 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -463,3 +463,19 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev) debugfs_remove_recursive(sdev->debugfs_root); } EXPORT_SYMBOL_GPL(snd_sof_free_debug); + +void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) +{ + if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || + (sof_core_debug & SOF_DBG_RETAIN_CTX)) { + /* should we prevent DSP entering D3 ? */ + dev_info(sdev->dev, "info: preventing DSP entering D3 state to preserve context\n"); + pm_runtime_get_noresume(sdev->dev); + } + + /* dump vital information to the logs */ + snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); + snd_sof_ipc_dump(sdev); + snd_sof_trace_notify_for_error(sdev); +} +EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 5acae75f5750..71f318bc2c74 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -11,8 +11,8 @@ config SND_SOC_SOF_IMX_TOPLEVEL if SND_SOC_SOF_IMX_TOPLEVEL -config SND_SOC_SOF_IMX8 - tristate "SOF support for i.MX8" +config SND_SOC_SOF_IMX8_SUPPORT + bool "SOF support for i.MX8" depends on IMX_SCU depends on IMX_DSP help @@ -20,4 +20,8 @@ config SND_SOC_SOF_IMX8 Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_IMX8 + def_tristate SND_SOC_SOF_OF + depends on SND_SOC_SOF_IMX8_SUPPORT + endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 2a22b18e5ec0..cfefcfd92798 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -388,6 +388,13 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* DAI drivers */ .drv = imx8_dai, .num_drv = 1, /* we have only 1 ESAI interface on i.MX8 */ + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; EXPORT_SYMBOL(sof_imx8_ops); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index d62f51d33be1..92f7485b6994 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -10,7 +10,7 @@ config SND_SOC_SOF_INTEL_TOPLEVEL if SND_SOC_SOF_INTEL_TOPLEVEL config SND_SOC_SOF_INTEL_ACPI - tristate + def_tristate SND_SOC_SOF_ACPI select SND_SOC_SOF_BAYTRAIL if SND_SOC_SOF_BAYTRAIL_SUPPORT select SND_SOC_SOF_BROADWELL if SND_SOC_SOF_BROADWELL_SUPPORT help @@ -18,7 +18,7 @@ config SND_SOC_SOF_INTEL_ACPI 'select' statements at a higher level config SND_SOC_SOF_INTEL_PCI - tristate + def_tristate SND_SOC_SOF_PCI select SND_SOC_SOF_MERRIFIELD if SND_SOC_SOF_MERRIFIELD_SUPPORT select SND_SOC_SOF_APOLLOLAKE if SND_SOC_SOF_APOLLOLAKE_SUPPORT select SND_SOC_SOF_GEMINILAKE if SND_SOC_SOF_GEMINILAKE_SUPPORT @@ -29,6 +29,7 @@ config SND_SOC_SOF_INTEL_PCI select SND_SOC_SOF_COMETLAKE_H if SND_SOC_SOF_COMETLAKE_H_SUPPORT select SND_SOC_SOF_TIGERLAKE if SND_SOC_SOF_TIGERLAKE_SUPPORT select SND_SOC_SOF_ELKHARTLAKE if SND_SOC_SOF_ELKHARTLAKE_SUPPORT + select SND_SOC_SOF_JASPERLAKE if SND_SOC_SOF_JASPERLAKE_SUPPORT help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -61,10 +62,18 @@ if SND_SOC_SOF_INTEL_ACPI config SND_SOC_SOF_BAYTRAIL_SUPPORT bool "SOF support for Baytrail, Braswell and Cherrytrail" + depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI=n help This adds support for Sound Open Firmware for Intel(R) platforms using the Baytrail, Braswell or Cherrytrail processors. - Say Y if you have such a device. + This option is mutually exclusive with the Atom/SST and Baytrail + legacy drivers. If you want to enable SOF on Baytrail/Cherrytrail, + you need to deselect those options first. + SOF does not support Baytrail-CR for now, so this option is not + recommended for distros. At some point all legacy drivers will be + deprecated but not before all userspace firmware/topology/UCM files + are made available to downstream distros. + Say Y if you want to enable SOF on Baytrail/Cherrytrail If unsure select "N". config SND_SOC_SOF_BAYTRAIL @@ -76,10 +85,18 @@ config SND_SOC_SOF_BAYTRAIL config SND_SOC_SOF_BROADWELL_SUPPORT bool "SOF support for Broadwell" + depends on SND_SOC_INTEL_HASWELL=n help This adds support for Sound Open Firmware for Intel(R) platforms using the Broadwell processors. - Say Y if you have such a device. + This option is mutually exclusive with the Haswell/Broadwell legacy + driver. If you want to enable SOF on Broadwell you need to deselect + the legacy driver first. + SOF does fully support Broadwell yet, so this option is not + recommended for distros. At some point all legacy drivers will be + deprecated but not before all userspace firmware/topology/UCM files + are made available to downstream distros. + Say Y if you want to enable SOF on Broadwell If unsure select "N". config SND_SOC_SOF_BROADWELL @@ -244,6 +261,21 @@ config SND_SOC_SOF_ELKHARTLAKE This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_JASPERLAKE_SUPPORT + bool "SOF support for JasperLake" + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the JasperLake processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_JASPERLAKE + tristate + select SND_SOC_SOF_HDA_COMMON + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + config SND_SOC_SOF_HDA_COMMON tristate select SND_SOC_SOF_INTEL_COMMON @@ -283,6 +315,16 @@ config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1 Say Y if you want to enable DMI Link L1 If unsure, select "N". +config SND_SOC_SOF_HDA_COMMON_HDMI_CODEC + bool "SOF common HDA HDMI codec driver" + depends on SND_SOC_SOF_HDA_LINK + depends on SND_HDA_CODEC_HDMI + help + This adds support for HDMI audio by using the common HDA + HDMI/DisplayPort codec driver. + Say Y if you want to use the common codec driver with SOF. + If unsure select "Y". + endif ## SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK_BASELINE diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 8dc7a5558da4..7daa8eb456c8 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -97,6 +97,14 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .runtime_resume = hda_dsp_runtime_resume, .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, + .set_power_state = hda_dsp_set_power_state, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 80e2826fb447..141dad554764 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -247,7 +247,7 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[BDW_STACK_DUMP_SIZE]; - u32 status, panic; + u32 status, panic, imrx, imrd; /* now try generic SOF status messages */ status = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCD); @@ -256,6 +256,26 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) BDW_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, BDW_STACK_DUMP_SIZE); + + /* provide some context for firmware debug */ + imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX); + imrd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRD); + dev_err(sdev->dev, + "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n", + (panic & SHIM_IPCX_BUSY) ? "yes" : "no", + (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic); + dev_err(sdev->dev, + "error: mask host: pending %s complete %s raw 0x%8.8x\n", + (imrx & SHIM_IMRX_BUSY) ? "yes" : "no", + (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx); + dev_err(sdev->dev, + "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n", + (status & SHIM_IPCD_BUSY) ? "yes" : "no", + (status & SHIM_IPCD_DONE) ? "yes" : "no", status); + dev_err(sdev->dev, + "error: mask DSP: pending %s complete %s raw 0x%8.8x\n", + (imrd & SHIM_IMRD_BUSY) ? "yes" : "no", + (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd); } /* @@ -571,7 +591,14 @@ const struct snd_sof_dsp_ops sof_bdw_ops = { /* DAI drivers */ .drv = bdw_dai, - .num_drv = ARRAY_SIZE(bdw_dai) + .num_drv = ARRAY_SIZE(bdw_dai), + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, }; EXPORT_SYMBOL(sof_bdw_ops); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index a1e514f71739..2abf80b3eb52 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -145,7 +145,7 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags) struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[BYT_STACK_DUMP_SIZE]; - u32 status, panic; + u32 status, panic, imrd, imrx; /* now try generic SOF status messages */ status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); @@ -154,6 +154,27 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags) BYT_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, BYT_STACK_DUMP_SIZE); + + /* provide some context for firmware debug */ + imrx = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRX); + imrd = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRD); + dev_err(sdev->dev, + "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n", + (panic & SHIM_IPCX_BUSY) ? "yes" : "no", + (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic); + dev_err(sdev->dev, + "error: mask host: pending %s complete %s raw 0x%8.8x\n", + (imrx & SHIM_IMRX_BUSY) ? "yes" : "no", + (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx); + dev_err(sdev->dev, + "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n", + (status & SHIM_IPCD_BUSY) ? "yes" : "no", + (status & SHIM_IPCD_DONE) ? "yes" : "no", status); + dev_err(sdev->dev, + "error: mask DSP: pending %s complete %s raw 0x%8.8x\n", + (imrd & SHIM_IMRD_BUSY) ? "yes" : "no", + (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd); + } /* @@ -511,6 +532,13 @@ const struct snd_sof_dsp_ops sof_tng_ops = { /* DAI drivers */ .drv = byt_dai, .num_drv = 3, /* we have only 3 SSPs on byt*/ + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, }; EXPORT_SYMBOL(sof_tng_ops); @@ -672,6 +700,13 @@ const struct snd_sof_dsp_ops sof_byt_ops = { /* DAI drivers */ .drv = byt_dai, .num_drv = 3, /* we have only 3 SSPs on byt*/ + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, }; EXPORT_SYMBOL(sof_byt_ops); @@ -732,6 +767,13 @@ const struct snd_sof_dsp_ops sof_cht_ops = { .drv = byt_dai, /* all 6 SSPs may be available for cherrytrail */ .num_drv = ARRAY_SIZE(byt_dai), + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, }; EXPORT_SYMBOL(sof_cht_ops); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 4ddd73762d81..0e1e265f3f3b 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -17,6 +17,7 @@ #include "../ops.h" #include "hda.h" +#include "hda-ipc.h" static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, @@ -150,14 +151,45 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev) CNL_DSP_REG_HIPCCTL_DONE); } +static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, + u32 *dr, u32 *dd) +{ + struct sof_ipc_pm_gate *pm_gate; + + if (msg->header == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { + pm_gate = msg->msg_data; + + /* send the compact message via the primary register */ + *dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE; + + /* send payload via the extended data register */ + *dd = pm_gate->flags; + + return true; + } + + return false; +} + static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - /* send the message */ - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, - CNL_DSP_REG_HIPCIDR_BUSY); + u32 dr = 0; + u32 dd = 0; + + if (cnl_compact_ipc_compress(msg, &dr, &dd)) { + /* send the message via IPC registers */ + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, + dd); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + CNL_DSP_REG_HIPCIDR_BUSY | dr); + } else { + /* send the message via mailbox */ + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + CNL_DSP_REG_HIPCIDR_BUSY); + } return 0; } @@ -255,6 +287,14 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .runtime_resume = hda_dsp_runtime_resume, .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, + .set_power_state = hda_dsp_set_power_state, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; EXPORT_SYMBOL(sof_cnl_ops); @@ -327,3 +367,20 @@ const struct sof_intel_dsp_desc ehl_chip_info = { .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; EXPORT_SYMBOL(ehl_chip_info); + +const struct sof_intel_dsp_desc jsl_chip_info = { + /* Jasperlake */ + .cores_num = 2, + .init_core_mask = 1, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_init_timeout = 300, + .ssp_count = ICL_SSP_COUNT, + .ssp_base_offset = CNL_SSP_BASE_OFFSET, +}; +EXPORT_SYMBOL(jsl_chip_info); diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 3ca6795a89ba..827f84a0722e 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -84,6 +84,8 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) struct hdac_hda_priv *hda_priv; + struct snd_soc_acpi_mach_params *mach_params = NULL; + struct snd_sof_pdata *pdata = sdev->pdata; #endif struct hda_bus *hbus = sof_to_hbus(sdev); struct hdac_device *hdev; @@ -113,8 +115,19 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address) if (ret < 0) return ret; - /* use legacy bus only for HDA codecs, idisp uses ext bus */ - if ((resp & 0xFFFF0000) != IDISP_VID_INTEL) { + if (pdata->machine) + mach_params = (struct snd_soc_acpi_mach_params *) + &pdata->machine->mach_params; + + if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) + hda_priv->need_display_power = true; + + /* + * if common HDMI codec driver is not used, codec load + * is skipped here and hdac_hdmi is used instead + */ + if ((mach_params && mach_params->common_hdmi_codec_drv) || + (resp & 0xFFFF0000) != IDISP_VID_INTEL) { hdev->type = HDA_DEV_LEGACY; hda_codec_load_module(&hda_priv->codec); } @@ -155,7 +168,8 @@ int hda_codec_probe_bus(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(hda_codec_probe_bus); -#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ + IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) void hda_codec_i915_get(struct snd_sof_dev *sdev) { @@ -204,6 +218,6 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(hda_codec_i915_exit); -#endif /* CONFIG_SND_SOC_HDAC_HDMI */ +#endif MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index fb55a3c5afd0..4a4d318f97ff 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -19,6 +19,7 @@ #include <sound/hda_register.h> #include "../ops.h" #include "hda.h" +#include "hda-ipc.h" /* * DSP Core control. @@ -42,6 +43,12 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) ((adspcs & reset) == reset), HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", + __func__); + return ret; + } /* has core entered reset ? */ adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -77,6 +84,13 @@ int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", + __func__); + return ret; + } + /* has core left reset ? */ adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); @@ -151,8 +165,12 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) (adspcs & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); - if (ret < 0) - dev_err(sdev->dev, "error: timeout on core powerup\n"); + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", + __func__); + return ret; + } /* did core power up ? */ adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -171,17 +189,24 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { u32 adspcs; + int ret; /* update bits */ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); - return snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, adspcs, !(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)), HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); + if (ret < 0) + dev_err(sdev->dev, + "error: %s: timeout on HDA_DSP_REG_ADSPCS read\n", + __func__); + + return ret; } bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, @@ -282,6 +307,80 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0); } +static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + int retry = HDA_DSP_REG_POLL_RETRY_COUNT; + + while (snd_hdac_chip_readb(bus, VS_D0I3C) & SOF_HDA_VS_D0I3C_CIP) { + if (!retry--) + return -ETIMEDOUT; + usleep_range(10, 15); + } + + return 0; +} + +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; + + 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(sdev->ipc, pm_gate.hdr.cmd, &pm_gate, + sizeof(pm_gate), &reply, sizeof(reply)); +} + +int hda_dsp_set_power_state(struct snd_sof_dev *sdev, + enum sof_d0_substate d0_substate) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + u32 flags; + int ret; + u8 value; + + /* Write to D0I3C after Command-In-Progress bit is cleared */ + ret = hda_dsp_wait_d0i3c_done(sdev); + if (ret < 0) { + dev_err(bus->dev, "CIP timeout before D0I3C update!\n"); + return ret; + } + + /* Update D0I3C register */ + value = d0_substate == SOF_DSP_D0I3 ? SOF_HDA_VS_D0I3C_I3 : 0; + snd_hdac_chip_updateb(bus, VS_D0I3C, SOF_HDA_VS_D0I3C_I3, value); + + /* Wait for cmd in progress to be cleared before exiting the function */ + ret = hda_dsp_wait_d0i3c_done(sdev); + if (ret < 0) { + dev_err(bus->dev, "CIP timeout after D0I3C update!\n"); + return ret; + } + + dev_vdbg(bus->dev, "D0I3C updated, register = 0x%x\n", + snd_hdac_chip_readb(bus, VS_D0I3C)); + + if (d0_substate == SOF_DSP_D0I0) + flags = HDA_PM_PPG;/* prevent power gating in D0 */ + else + flags = HDA_PM_NO_DMA_TRACE;/* disable DMA trace in D0I3*/ + + /* sending pm_gate IPC */ + ret = hda_dsp_send_pm_gate_ipc(sdev, flags); + if (ret < 0) + dev_err(sdev->dev, + "error: PM_GATE ipc error %d\n", ret); + + return ret; +} + static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -379,6 +478,22 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) int hda_dsp_resume(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct pci_dev *pci = to_pci_dev(sdev->dev); + + if (sdev->s0_suspend) { + /* restore L1SEN bit */ + if (hda->l1_support_changed) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, 0); + + /* restore and disable the system wakeup */ + pci_restore_state(pci); + disable_irq_wake(pci->irq); + return 0; + } + /* init hda controller. DSP cores will be powered up during fw boot */ return hda_resume(sdev, false); } @@ -410,9 +525,25 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev) int hda_dsp_suspend(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct hdac_bus *bus = sof_to_bus(sdev); + struct pci_dev *pci = to_pci_dev(sdev->dev); int ret; + if (sdev->s0_suspend) { + /* enable L1SEN to make sure the system can enter S0Ix */ + hda->l1_support_changed = + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + HDA_VS_INTEL_EM2, + HDA_VS_INTEL_EM2_L1SEN, + HDA_VS_INTEL_EM2_L1SEN); + + /* enable the system waking up via IPC IRQ */ + enable_irq_wake(pci->irq); + pci_save_state(pci); + return 0; + } + /* stop hda controller and power dsp off */ ret = hda_suspend(sdev, false); if (ret < 0) { diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 6aae6f18b3dc..0fd2153c1769 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -83,10 +83,12 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) } hdr = msg->msg_data; - if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) { + if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) || + hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) { /* * memory windows are powered off before sending IPC reply, - * so we can't read the mailbox for CTX_SAVE reply. + * so we can't read the mailbox for CTX_SAVE and PM_GATE + * replies. */ reply.error = 0; reply.hdr.cmd = SOF_IPC_GLB_REPLY; diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h new file mode 100644 index 000000000000..aef0ceac9803 --- /dev/null +++ b/sound/soc/sof/intel/hda-ipc.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: (GPL-2.0 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) 2019 Intel Corporation. All rights reserved. + * + * Author: Keyon Jie <yang.jie@linux.intel.com> + */ + +#ifndef __SOF_INTEL_HDA_IPC_H +#define __SOF_INTEL_HDA_IPC_H + +/* + * Primary register, mapped to + * - DIPCTDR (HIPCIDR) in sideband IPC (cAVS 1.8+) + * - DIPCT in cAVS 1.5 IPC + * + * Secondary register, mapped to: + * - DIPCTDD (HIPCIDD) in sideband IPC (cAVS 1.8+) + * - DIPCTE in cAVS 1.5 IPC + */ + +/* Common bits in primary register */ + +/* Reserved for doorbell */ +#define HDA_IPC_RSVD_31 BIT(31) +/* Target, 0 - normal message, 1 - compact message(cAVS compatible) */ +#define HDA_IPC_MSG_COMPACT BIT(30) +/* Direction, 0 - request, 1 - response */ +#define HDA_IPC_RSP BIT(29) + +#define HDA_IPC_TYPE_SHIFT 24 +#define HDA_IPC_TYPE_MASK GENMASK(28, 24) +#define HDA_IPC_TYPE(x) ((x) << HDA_IPC_TYPE_SHIFT) + +#define HDA_IPC_PM_GATE HDA_IPC_TYPE(0x8U) + +/* Command specific payload bits in secondary register */ + +/* Disable DMA tracing (0 - keep tracing, 1 - to disable DMA trace) */ +#define HDA_PM_NO_DMA_TRACE BIT(4) +/* Prevent clock gating (0 - cg allowed, 1 - DSP clock always on) */ +#define HDA_PM_PCG BIT(3) +/* Prevent power gating (0 - deep power state transitions allowed) */ +#define HDA_PM_PPG BIT(2) +/* Indicates whether streaming is active */ +#define HDA_PM_PG_STREAMING BIT(1) +#define HDA_PM_PG_RSVD BIT(0) + +#endif diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 65c2af3fcaab..b1783360fe10 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -126,7 +126,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, HDA_DSP_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(sdev->dev, "error: waiting for HIPCIE done\n"); + dev_err(sdev->dev, "error: %s: timeout for HIPCIE done\n", + __func__); goto err; } @@ -152,6 +153,10 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, if (!ret) return 0; + dev_err(sdev->dev, + "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", + __func__); + err: hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); hda_dsp_core_reset_power_down(sdev, chip->cores_mask); @@ -253,10 +258,22 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_BASEFW_TIMEOUT_US); + /* + * even in case of errors we still need to stop the DMAs, + * but we return the initial error should the DMA stop also fail + */ + + if (status < 0) { + dev_err(sdev->dev, + "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", + __func__); + } + ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP); if (ret < 0) { dev_err(sdev->dev, "error: DMA trigger stop failed\n"); - return ret; + if (!status) + status = ret; } return status; @@ -341,13 +358,15 @@ cleanup: /* * Perform codeloader stream cleanup. * This should be done even if firmware loading fails. + * If the cleanup also fails, we return the initial error */ ret1 = cl_cleanup(sdev, &sdev->dmab, stream); if (ret1 < 0) { dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); /* set return value to indicate cleanup failure */ - ret = ret1; + if (!ret) + ret = ret1; } /* diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 9b730f183529..575f5f5877d8 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -89,6 +89,7 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_dma_buffer *dmab; + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; int ret; u32 size, rate, bits; @@ -116,9 +117,17 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, /* disable SPIB, to enable buffer wrap for stream */ hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); - /* set host_period_bytes to 0 if no IPC position */ - if (hda && hda->no_ipc_position) - ipc_params->host_period_bytes = 0; + /* update no_stream_position flag for ipc params */ + if (hda && hda->no_ipc_position) { + /* For older ABIs set host_period_bytes to zero to inform + * FW we don't want position updates. Newer versions use + * no_stream_position for this purpose. + */ + if (v->abi_version < SOF_ABI_VER(3, 10, 0)) + ipc_params->host_period_bytes = 0; + else + ipc_params->no_stream_position = 1; + } ipc_params->stream_tag = hstream->stream_tag; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 0c11fceb28a7..29ab43281670 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -275,8 +275,12 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret) + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", + __func__, cmd); return ret; + } hstream->running = true; break; @@ -294,8 +298,12 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret) + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", + __func__, cmd); return ret; + } snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, @@ -356,8 +364,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret) + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: timeout on STREAM_SD_OFFSET read1\n", + __func__); return ret; + } snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, @@ -418,8 +430,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret) + if (ret < 0) { + dev_err(sdev->dev, + "error: %s: timeout on STREAM_SD_OFFSET read2\n", + __func__); return ret; + } snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 06e84679087b..91bd88fddac7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -32,9 +32,6 @@ /* platform specific devices */ #include "shim.h" -#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348) -#define IS_CNL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9dc8) - #define EXCEPT_MAX_HDR_SIZE 0x400 /* @@ -56,6 +53,11 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode"); static int hda_dmic_num = -1; module_param_named(dmic_num, hda_dmic_num, int, 0444); MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number"); + +static bool hda_codec_use_common_hdmi = + IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_COMMON_HDMI_CODEC); +module_param_named(use_common_hdmi, hda_codec_use_common_hdmi, bool, 0444); +MODULE_PARM_DESC(use_common_hdmi, "SOF HDA use common HDMI codec driver"); #endif static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { @@ -262,12 +264,9 @@ static int hda_init(struct snd_sof_dev *sdev) /* HDA bus init */ sof_hda_bus_init(bus, &pci->dev); - /* Workaround for a communication error on CFL (bko#199007) and CNL */ - if (IS_CFL(pci) || IS_CNL(pci)) - bus->polling_mode = 1; - bus->use_posbuf = 1; bus->bdl_pos_adj = 0; + bus->sync_write = 1; mutex_init(&hbus->prepare_mutex); hbus->pci = pci; @@ -416,9 +415,16 @@ static int hda_init_caps(struct snd_sof_dev *sdev) pdata->tplg_filename = hda_mach->sof_tplg_filename; - /* firmware: pick the first in machine list */ + /* + * firmware: pick the first in machine list, + * or use nocodec firmware name if list is empty + */ mach = pdata->desc->machines; - pdata->fw_filename = mach->sof_fw_filename; + if (mach->id[0]) + pdata->fw_filename = mach->sof_fw_filename; + else + pdata->fw_filename = + pdata->desc->nocodec_fw_filename; dev_info(bus->dev, "using HDA machine driver %s now\n", hda_mach->drv_name); @@ -465,6 +471,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev) &pdata->machine->mach_params; mach_params->codec_mask = bus->codec_mask; mach_params->platform = dev_name(sdev->dev); + mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; } /* create codec instances */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 23e430d3e056..18d7e72bf9b7 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -64,6 +64,13 @@ #define SOF_HDA_PPCTL_PIE BIT(31) #define SOF_HDA_PPCTL_GPROCEN BIT(30) +/*Vendor Specific Registers*/ +#define SOF_HDA_VS_D0I3C 0x104A + +/* D0I3C Register fields */ +#define SOF_HDA_VS_D0I3C_CIP BIT(0) /* Command-In-Progress */ +#define SOF_HDA_VS_D0I3C_I3 BIT(2) /* D0i3 enable bit */ + /* DPIB entry size: 8 Bytes = 2 DWords */ #define SOF_HDA_DPIB_ENTRY_SIZE 0x8 @@ -207,6 +214,7 @@ #define HDA_DSP_CTRL_RESET_TIMEOUT 100 #define HDA_DSP_WAIT_TIMEOUT 500 /* 500 msec */ #define HDA_DSP_REG_POLL_INTERVAL_US 500 /* 0.5 msec */ +#define HDA_DSP_REG_POLL_RETRY_COUNT 50 #define HDA_DSP_ADSPIC_IPC 1 #define HDA_DSP_ADSPIS_IPC 1 @@ -304,6 +312,7 @@ #define CNL_DSP_REG_HIPCTDD (CNL_DSP_IPC_BASE + 0x08) #define CNL_DSP_REG_HIPCIDR (CNL_DSP_IPC_BASE + 0x10) #define CNL_DSP_REG_HIPCIDA (CNL_DSP_IPC_BASE + 0x14) +#define CNL_DSP_REG_HIPCIDD (CNL_DSP_IPC_BASE + 0x18) #define CNL_DSP_REG_HIPCCTL (CNL_DSP_IPC_BASE + 0x28) /* HIPCI */ @@ -399,6 +408,9 @@ struct sof_intel_hda_dev { int irq; + /* PM related */ + bool l1_support_changed;/* during suspend, is L1SEN changed or not */ + /* DMIC device */ struct platform_device *dmic_dev; }; @@ -455,6 +467,9 @@ int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); +int hda_dsp_set_power_state(struct snd_sof_dev *sdev, + enum sof_d0_substate d0_substate); + int hda_dsp_suspend(struct snd_sof_dev *sdev); int hda_dsp_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); @@ -565,7 +580,9 @@ void hda_codec_jack_check(struct snd_sof_dev *sdev); #endif /* CONFIG_SND_SOC_SOF_HDA */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) && \ + (IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ + IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) void hda_codec_i915_get(struct snd_sof_dev *sdev); void hda_codec_i915_put(struct snd_sof_dev *sdev); @@ -579,7 +596,7 @@ static inline void hda_codec_i915_put(struct snd_sof_dev *sdev) { } static inline int hda_codec_i915_init(struct snd_sof_dev *sdev) { return 0; } static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } -#endif /* CONFIG_SND_SOC_SOF_HDA && CONFIG_SND_SOC_HDAC_HDMI */ +#endif /* * Trace Control. @@ -596,7 +613,6 @@ extern struct snd_soc_dai_driver skl_dai[]; */ extern const struct snd_sof_dsp_ops sof_apl_ops; extern const struct snd_sof_dsp_ops sof_cnl_ops; -extern const struct snd_sof_dsp_ops sof_skl_ops; extern const struct sof_intel_dsp_desc apl_chip_info; extern const struct sof_intel_dsp_desc cnl_chip_info; @@ -604,5 +620,6 @@ extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc icl_chip_info; extern const struct sof_intel_dsp_desc tgl_chip_info; extern const struct sof_intel_dsp_desc ehl_chip_info; +extern const struct sof_intel_dsp_desc jsl_chip_info; #endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 086eeeab8679..5994e1073364 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -210,9 +210,7 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, if (ret == 0) { dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n", hdr->cmd, hdr->size); - snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX); - snd_sof_ipc_dump(ipc->sdev); - snd_sof_trace_notify_for_error(ipc->sdev); + snd_sof_handle_fw_exception(ipc->sdev); ret = -ETIMEDOUT; } else { /* copy the data returned from DSP */ @@ -796,12 +794,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) struct snd_sof_ipc *ipc; struct snd_sof_ipc_msg *msg; - /* check if mandatory ops required for ipc are defined */ - if (!sof_ops(sdev)->fw_ready) { - dev_err(sdev->dev, "error: ipc mandatory ops not defined\n"); - return NULL; - } - ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); if (!ipc) return NULL; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 824d36fe59fd..93512dcbaacd 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -193,6 +193,16 @@ static inline int snd_sof_dsp_set_clk(struct snd_sof_dev *sdev, u32 freq) return 0; } +static inline int snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, + enum sof_d0_substate substate) +{ + if (sof_ops(sdev)->set_power_state) + return sof_ops(sdev)->set_power_state(sdev, substate); + + /* D0 substate is not supported */ + return -ENOTSUPP; +} + /* debug */ static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) { diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 2b876d497447..3d5cd1b445ba 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -19,12 +19,11 @@ #define DRV_NAME "sof-audio-component" /* Create DMA buffer page table for DSP */ -static int create_page_table(struct snd_pcm_substream *substream, +static int create_page_table(struct snd_soc_component *component, + struct snd_pcm_substream *substream, unsigned char *dma_area, size_t size) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); @@ -95,13 +94,12 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); /* this may get called several times by oss emulation */ -static int sof_pcm_hw_params(struct snd_pcm_substream *substream, +static int sof_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; struct sof_ipc_pcm_params pcm; @@ -135,7 +133,7 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, * ret == 0 means the buffer is not changed * so no need to regenerate the page table */ - ret = create_page_table(substream, runtime->dma_area, + ret = create_page_table(component, substream, runtime->dma_area, runtime->dma_bytes); if (ret < 0) return ret; @@ -237,11 +235,10 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, return ret; } -static int sof_pcm_hw_free(struct snd_pcm_substream *substream) +static int sof_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; int ret, err = 0; @@ -276,11 +273,10 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream) return err; } -static int sof_pcm_prepare(struct snd_pcm_substream *substream) +static int sof_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; int ret; @@ -300,7 +296,8 @@ static int sof_pcm_prepare(struct snd_pcm_substream *substream) substream->stream); /* set hw_params */ - ret = sof_pcm_hw_params(substream, &spcm->params[substream->stream]); + ret = sof_pcm_hw_params(component, + substream, &spcm->params[substream->stream]); if (ret < 0) { dev_err(sdev->dev, "error: set pcm hw_params after resume\n"); return ret; @@ -313,11 +310,10 @@ static int sof_pcm_prepare(struct snd_pcm_substream *substream) * FE dai link trigger actions are always executed in non-atomic context because * they involve IPC's. */ -static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int sof_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; struct sof_ipc_stream stream; @@ -350,8 +346,18 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; case SNDRV_PCM_TRIGGER_RESUME: + if (spcm->stream[substream->stream].suspend_ignored) { + /* + * this case will be triggered when INFO_RESUME is + * supported, no need to resume streams that remained + * enabled in D0ix. + */ + spcm->stream[substream->stream].suspend_ignored = false; + return 0; + } + /* set up hw_params */ - ret = sof_pcm_prepare(substream); + ret = sof_pcm_prepare(component, substream); if (ret < 0) { dev_err(sdev->dev, "error: failed to set up hw_params upon resume\n"); @@ -360,9 +366,30 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) /* fallthrough */ case SNDRV_PCM_TRIGGER_START: + if (spcm->stream[substream->stream].suspend_ignored) { + /* + * This case will be triggered when INFO_RESUME is + * not supported, no need to re-start streams that + * remained enabled in D0ix. + */ + spcm->stream[substream->stream].suspend_ignored = false; + return 0; + } stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; break; case SNDRV_PCM_TRIGGER_SUSPEND: + if (sdev->s0_suspend && + spcm->stream[substream->stream].d0i3_compatible) { + /* + * trap the event, not sending trigger stop to + * prevent the FW pipelines from being stopped, + * and mark the flag to ignore the upcoming DAPM + * PM events. + */ + spcm->stream[substream->stream].suspend_ignored = true; + return 0; + } + /* fallthrough */ case SNDRV_PCM_TRIGGER_STOP: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; ipc_first = true; @@ -395,11 +422,10 @@ static int sof_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; @@ -428,13 +454,24 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_pcm_substream *substream) return host; } -static int sof_pcm_open(struct snd_pcm_substream *substream) +#ifdef CONFIG_SND_DMA_SGBUF +static struct page *sof_pcm_page(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + unsigned long offset) +{ + return snd_pcm_sgbuf_ops_page(substream, offset); +} +#else +#define sof_pcm_page NULL +#endif /* CONFIG_SND_DMA_SGBUF */ + +static int sof_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct snd_sof_dsp_ops *ops = sof_ops(sdev); struct snd_sof_pcm *spcm; struct snd_soc_tplg_stream_caps *caps; int ret; @@ -464,11 +501,8 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) le32_to_cpu(caps->period_size_min)); /* set runtime config */ - runtime->hw.info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; + runtime->hw.info = ops->hw_info; /* platform-specific */ + runtime->hw.formats = le64_to_cpu(caps->formats); runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min); runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max); @@ -505,11 +539,10 @@ static int sof_pcm_open(struct snd_pcm_substream *substream) return ret; } -static int sof_pcm_close(struct snd_pcm_substream *substream) +static int sof_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; int err; @@ -538,27 +571,14 @@ static int sof_pcm_close(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops sof_pcm_ops = { - .open = sof_pcm_open, - .close = sof_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = sof_pcm_hw_params, - .prepare = sof_pcm_prepare, - .hw_free = sof_pcm_hw_free, - .trigger = sof_pcm_trigger, - .pointer = sof_pcm_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - /* * Pre-allocate playback/capture audio buffer pages. * no need to explicitly release memory preallocated by sof_pcm_new in pcm_free * snd_pcm_lib_preallocate_free_for_all() is called by the core. */ -static int sof_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int sof_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_sof_pcm *spcm; struct snd_pcm *pcm = rtd->pcm; @@ -691,6 +711,14 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, case SOF_DAI_INTEL_ALH: /* do nothing for ALH dai_link */ break; + case SOF_DAI_IMX_ESAI: + channels->min = dai->dai_config->esai.tdm_slots; + channels->max = dai->dai_config->esai.tdm_slots; + + dev_dbg(sdev->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(sdev->dev, "error: invalid DAI type %d\n", dai->dai_config->type); @@ -752,11 +780,20 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->name = "sof-audio-component"; pd->probe = sof_pcm_probe; pd->remove = sof_pcm_remove; - pd->ops = &sof_pcm_ops; + pd->open = sof_pcm_open; + pd->close = sof_pcm_close; + pd->ioctl = snd_soc_pcm_lib_ioctl; + pd->hw_params = sof_pcm_hw_params; + pd->prepare = sof_pcm_prepare; + pd->hw_free = sof_pcm_hw_free; + pd->trigger = sof_pcm_trigger; + pd->pointer = sof_pcm_pointer; + pd->page = sof_pcm_page; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) pd->compr_ops = &sof_compressed_ops; #endif - pd->pcm_new = sof_pcm_new; + pd->pcm_construct = sof_pcm_new; pd->ignore_machine = drv_name; pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; pd->be_pcm_base = SOF_BE_PCM_BASE; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index e23beaeefe00..0fd5567237a8 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -197,7 +197,7 @@ static int sof_restore_pipelines(struct snd_sof_dev *sdev) return ret; } -static int sof_send_pm_ipc(struct snd_sof_dev *sdev, int cmd) +static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) { struct sof_ipc_pm_ctx pm_ctx; struct sof_ipc_reply reply; @@ -320,12 +320,15 @@ static int sof_resume(struct device *dev, bool runtime_resume) } /* notify DSP of system resume */ - ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); + ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); if (ret < 0) dev_err(sdev->dev, "error: ctx_restore ipc error during resume %d\n", ret); + /* initialize default D0 sub-state */ + sdev->d0_substate = SOF_DSP_D0I0; + return ret; } @@ -358,7 +361,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) sof_cache_debugfs(sdev); #endif /* notify DSP of upcoming power down */ - ret = sof_send_pm_ipc(sdev, SOF_IPC_PM_CTX_SAVE); + ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); if (ret == -EBUSY || ret == -EAGAIN) { /* * runtime PM has logic to handle -EBUSY/-EAGAIN so @@ -408,14 +411,135 @@ int snd_sof_runtime_resume(struct device *dev) } EXPORT_SYMBOL(snd_sof_runtime_resume); +int snd_sof_set_d0_substate(struct snd_sof_dev *sdev, + enum sof_d0_substate d0_substate) +{ + int ret; + + if (sdev->d0_substate == d0_substate) + return 0; + + /* do platform specific set_state */ + ret = snd_sof_dsp_set_power_state(sdev, d0_substate); + if (ret < 0) + return ret; + + /* update dsp D0 sub-state */ + sdev->d0_substate = d0_substate; + + return 0; +} +EXPORT_SYMBOL(snd_sof_set_d0_substate); + +/* + * Audio DSP states may transform as below:- + * + * D0I3 compatible stream + * Runtime +---------------------+ opened only, timeout + * suspend | +--------------------+ + * +------------+ D0(active) | | + * | | <---------------+ | + * | +--------> | | | + * | |Runtime +--^--+---------^--+--+ The last | | + * | |resume | | | | opened D0I3 | | + * | | | | | | compatible | | + * | | resume| | | | stream closed | | + * | | from | | D3 | | | | + * | | D3 | |suspend | | d0i3 | | + * | | | | | |suspend | | + * | | | | | | | | + * | | | | | | | | + * +-v---+-----------+--v-------+ | | +------+----v----+ + * | | | +-----------> | + * | D3 (suspended) | | | D0I3 +-----+ + * | | +--------------+ | | + * | | resume from | | | + * +-------------------^--------+ d0i3 suspend +----------------+ | + * | | + * | D3 suspend | + * +------------------------------------------------+ + * + * d0i3_suspend = s0_suspend && D0I3 stream opened, + * D3 suspend = !d0i3_suspend, + */ + int snd_sof_resume(struct device *dev) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret; + + if (snd_sof_dsp_d0i3_on_suspend(sdev)) { + /* resume from D0I3 */ + dev_dbg(sdev->dev, "DSP will exit from D0i3...\n"); + ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I0); + if (ret == -ENOTSUPP) { + /* fallback to resume from D3 */ + dev_dbg(sdev->dev, "D0i3 not supported, fall back to resume from D3...\n"); + goto d3_resume; + } else if (ret < 0) { + dev_err(sdev->dev, "error: failed to exit from D0I3 %d\n", + ret); + return ret; + } + + /* platform-specific resume from D0i3 */ + return snd_sof_dsp_resume(sdev); + } + +d3_resume: + /* resume from D3 */ return sof_resume(dev, false); } EXPORT_SYMBOL(snd_sof_resume); int snd_sof_suspend(struct device *dev) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + int ret; + + if (snd_sof_dsp_d0i3_on_suspend(sdev)) { + /* suspend to D0i3 */ + dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n"); + ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I3); + if (ret == -ENOTSUPP) { + /* fallback to D3 suspend */ + dev_dbg(sdev->dev, "D0i3 not supported, fall back to D3...\n"); + goto d3_suspend; + } else if (ret < 0) { + dev_err(sdev->dev, "error: failed to enter D0I3, %d\n", + ret); + return ret; + } + + /* platform-specific suspend to D0i3 */ + return snd_sof_dsp_suspend(sdev); + } + +d3_suspend: + /* suspend to D3 */ return sof_suspend(dev, false); } EXPORT_SYMBOL(snd_sof_suspend); + +int snd_sof_prepare(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + +#if defined(CONFIG_ACPI) + sdev->s0_suspend = acpi_target_system_state() == ACPI_STATE_S0; +#else + /* will suspend to S3 by default */ + sdev->s0_suspend = false; +#endif + + return 0; +} +EXPORT_SYMBOL(snd_sof_prepare); + +void snd_sof_complete(struct device *dev) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + + sdev->s0_suspend = false; +} +EXPORT_SYMBOL(snd_sof_complete); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index ea7b8b895412..df318f50dd0b 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -29,6 +29,12 @@ static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); +static int sof_acpi_debug; +module_param_named(sof_acpi_debug, sof_acpi_debug, int, 0444); +MODULE_PARM_DESC(sof_acpi_debug, "SOF ACPI debug options (0x0 all off)"); + +#define SOF_ACPI_DISABLE_PM_RUNTIME BIT(0) + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HASWELL) static const struct sof_dev_desc sof_acpi_haswell_desc = { .machines = snd_soc_acpi_intel_haswell_machines, @@ -121,6 +127,9 @@ static const struct dev_pm_ops sof_acpi_pm = { static void sof_acpi_probe_complete(struct device *dev) { + if (sof_acpi_debug & SOF_ACPI_DISABLE_PM_RUNTIME) + return; + /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); @@ -221,7 +230,8 @@ static int sof_acpi_probe(struct platform_device *pdev) static int sof_acpi_remove(struct platform_device *pdev) { - pm_runtime_disable(&pdev->dev); + if (!(sof_acpi_debug & SOF_ACPI_DISABLE_PM_RUNTIME)) + pm_runtime_disable(&pdev->dev); /* call sof helper for DSP hardware remove */ snd_sof_device_remove(&pdev->dev); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index d66412a77873..3252dbe277c8 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -29,6 +29,12 @@ static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); +static int sof_pci_debug; +module_param_named(sof_pci_debug, sof_pci_debug, int, 0444); +MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)"); + +#define SOF_PCI_DISABLE_PM_RUNTIME BIT(0) + #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) static const struct sof_dev_desc bxt_desc = { .machines = snd_soc_acpi_intel_bxt_machines, @@ -113,7 +119,7 @@ static const struct sof_dev_desc cnl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) static const struct sof_dev_desc cfl_desc = { - .machines = snd_soc_acpi_intel_cnl_machines, + .machines = snd_soc_acpi_intel_cfl_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -122,7 +128,7 @@ static const struct sof_dev_desc cfl_desc = { .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cnl.ri", + .nocodec_fw_filename = "sof-cfl.ri", .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, .arch_ops = &sof_xtensa_arch_ops @@ -133,7 +139,7 @@ static const struct sof_dev_desc cfl_desc = { IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) static const struct sof_dev_desc cml_desc = { - .machines = snd_soc_acpi_intel_cnl_machines, + .machines = snd_soc_acpi_intel_cml_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -142,7 +148,7 @@ static const struct sof_dev_desc cml_desc = { .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-cnl.ri", + .nocodec_fw_filename = "sof-cml.ri", .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, .arch_ops = &sof_xtensa_arch_ops @@ -167,42 +173,6 @@ static const struct sof_dev_desc icl_desc = { }; #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) -static const struct sof_dev_desc skl_desc = { - .machines = snd_soc_acpi_intel_skl_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = -1, - .resindex_imr_base = -1, - .irqindex_host_ipc = -1, - .resindex_dma_base = -1, - .chip_info = &skl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-skl.ri", - .nocodec_tplg_filename = "sof-skl-nocodec.tplg", - .ops = &sof_skl_ops, - .arch_ops = &sof_xtensa_arch_ops -}; -#endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) -static const struct sof_dev_desc kbl_desc = { - .machines = snd_soc_acpi_intel_kbl_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = -1, - .resindex_imr_base = -1, - .irqindex_host_ipc = -1, - .resindex_dma_base = -1, - .chip_info = &skl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .nocodec_fw_filename = "sof-kbl.ri", - .nocodec_tplg_filename = "sof-kbl-nocodec.tplg", - .ops = &sof_skl_ops, - .arch_ops = &sof_xtensa_arch_ops -}; -#endif - #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) static const struct sof_dev_desc tgl_desc = { .machines = snd_soc_acpi_intel_tgl_machines, @@ -239,7 +209,27 @@ static const struct sof_dev_desc ehl_desc = { }; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) +static const struct sof_dev_desc jsl_desc = { + .machines = snd_soc_acpi_intel_jsl_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .chip_info = &jsl_chip_info, + .default_fw_path = "intel/sof", + .default_tplg_path = "intel/sof-tplg", + .nocodec_fw_filename = "sof-jsl.ri", + .nocodec_tplg_filename = "sof-jsl-nocodec.tplg", + .ops = &sof_cnl_ops, + .arch_ops = &sof_xtensa_arch_ops +}; +#endif + static const struct dev_pm_ops sof_pci_pm = { + .prepare = snd_sof_prepare, + .complete = snd_sof_complete, SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, snd_sof_runtime_idle) @@ -249,6 +239,9 @@ static void sof_pci_probe_complete(struct device *dev) { dev_dbg(dev, "Completing SOF PCI probe"); + if (sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME) + return; + /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); @@ -370,7 +363,8 @@ static void sof_pci_remove(struct pci_dev *pci) snd_sof_device_remove(&pci->dev); /* follow recommendation in pci-driver.c to increment usage counter */ - pm_runtime_get_noresume(&pci->dev); + if (!(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME)) + pm_runtime_get_noresume(&pci->dev); /* release pci regions and disable device */ pci_release_regions(pci); @@ -401,18 +395,14 @@ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE(0x8086, 0xa348), .driver_data = (unsigned long)&cfl_desc}, #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_KABYLAKE) - { PCI_DEVICE(0x8086, 0x9d71), - .driver_data = (unsigned long)&kbl_desc}, -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_SKYLAKE) - { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&skl_desc}, -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) { PCI_DEVICE(0x8086, 0x34C8), .driver_data = (unsigned long)&icl_desc}, #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) + { PCI_DEVICE(0x8086, 0x38c8), + .driver_data = (unsigned long)&jsl_desc}, +#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP) { PCI_DEVICE(0x8086, 0x02c8), .driver_data = (unsigned long)&cml_desc}, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 730f3259dd02..c7c2c70ee4d0 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -15,6 +15,7 @@ #include <sound/hdaudio.h> #include <sound/soc.h> +#include <sound/control.h> #include <sound/sof.h> #include <sound/sof/stream.h> /* needs to be included before control.h */ @@ -28,10 +29,15 @@ #include <uapi/sound/sof/fw.h> /* debug flags */ -#define SOF_DBG_REGS BIT(1) -#define SOF_DBG_MBOX BIT(2) -#define SOF_DBG_TEXT BIT(3) -#define SOF_DBG_PCI BIT(4) +#define SOF_DBG_ENABLE_TRACE BIT(0) +#define SOF_DBG_REGS BIT(1) +#define SOF_DBG_MBOX BIT(2) +#define SOF_DBG_TEXT BIT(3) +#define SOF_DBG_PCI BIT(4) +#define SOF_DBG_RETAIN_CTX BIT(5) /* prevent DSP D3 on FW exception */ + +/* global debug state set by SOF_DBG_ flags */ +extern int sof_core_debug; /* max BARs mmaped devices can use */ #define SND_SOF_BARS 8 @@ -62,6 +68,12 @@ #define DMA_CHAN_INVALID 0xFFFFFFFF +/* DSP D0ix sub-state */ +enum sof_d0_substate { + SOF_DSP_D0I0 = 0, /* DSP default D0 substate */ + SOF_DSP_D0I3, /* DSP D0i3(low power) substate*/ +}; + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -128,7 +140,7 @@ struct snd_sof_dsp_ops { * FW ready checks for ABI compatibility and creates * memory windows at first boot */ - int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* optional */ + int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* mandatory */ /* connect pcm substream to a host stream */ int (*pcm_open)(struct snd_sof_dev *sdev, @@ -177,6 +189,8 @@ struct snd_sof_dsp_ops { int (*runtime_resume)(struct snd_sof_dev *sof_dev); /* optional */ int (*runtime_idle)(struct snd_sof_dev *sof_dev); /* optional */ int (*set_hw_params_upon_resume)(struct snd_sof_dev *sdev); /* optional */ + int (*set_power_state)(struct snd_sof_dev *sdev, + enum sof_d0_substate d0_substate); /* optional */ /* DSP clocking */ int (*set_clk)(struct snd_sof_dev *sof_dev, u32 freq); /* optional */ @@ -205,6 +219,9 @@ struct snd_sof_dsp_ops { /* DAI ops */ struct snd_soc_dai_driver *drv; int num_drv; + + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ + u32 hw_info; }; /* DSP architecture specific callbacks for oops and stack dumps */ @@ -293,6 +310,12 @@ struct snd_sof_pcm_stream { struct sof_ipc_stream_posn posn; struct snd_pcm_substream *substream; struct work_struct period_elapsed_work; + bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ + /* + * flag to indicate that the DSP pipelines should be kept + * active or not while suspending the stream + */ + bool suspend_ignored; }; /* ALSA SOF PCM device */ @@ -305,6 +328,12 @@ struct snd_sof_pcm { bool prepared[2]; /* PCM_PARAMS set successfully */ }; +struct snd_sof_led_control { + unsigned int use_led; + unsigned int direction; + unsigned int led_value; +}; + /* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_sof_dev *sdev; @@ -319,6 +348,8 @@ struct snd_sof_control { u32 *volume_table; /* volume table computed from tlv data*/ struct list_head list; /* list in sdev control list */ + + struct snd_sof_led_control led_ctl; }; /* ASoC SOF DAPM widget */ @@ -370,6 +401,11 @@ struct snd_sof_dev { */ struct snd_soc_component_driver plat_drv; + /* power states related */ + enum sof_d0_substate d0_substate; + /* flag to track if the intended power target of suspend is S0ix */ + bool s0_suspend; + /* DSP firmware boot */ wait_queue_head_t boot_wait; u32 boot_complete; @@ -434,6 +470,7 @@ struct snd_sof_dev { int dma_trace_pages; wait_queue_head_t trace_sleep; u32 host_offset; + u32 dtrace_is_supported; /* set with Kconfig or module parameter */ u32 dtrace_is_enabled; u32 dtrace_error; u32 dtrace_draining; @@ -455,6 +492,10 @@ int snd_sof_runtime_resume(struct device *dev); int snd_sof_runtime_idle(struct device *dev); int snd_sof_resume(struct device *dev); int snd_sof_suspend(struct device *dev); +int snd_sof_prepare(struct device *dev); +void snd_sof_complete(struct device *dev); +int snd_sof_set_d0_substate(struct snd_sof_dev *sdev, + enum sof_d0_substate d0_substate); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); @@ -512,6 +553,8 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, return NULL; } +bool snd_sof_dsp_d0i3_on_suspend(struct snd_sof_dev *sdev); + struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, const char *name); struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, @@ -575,6 +618,7 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); +void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); /* * Platform specific ops. diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 4452594c2e17..d82ab981e840 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -135,7 +135,9 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { struct snd_sof_widget *swidget = w->dobj.private; + int stream = SNDRV_PCM_STREAM_CAPTURE; struct snd_sof_dev *sdev; + struct snd_sof_pcm *spcm; int ret = 0; if (!swidget) @@ -146,11 +148,24 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, dev_dbg(sdev->dev, "received event %d for widget %s\n", event, w->name); + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname); + if (!spcm) { + dev_err(sdev->dev, "error: cannot find PCM for %s\n", + swidget->widget->name); + return -EINVAL; + } + /* process events */ switch (event) { case SND_SOC_DAPM_PRE_PMU: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(sdev->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); + return 0; + } + /* set pcm params */ - ret = ipc_pcm_params(swidget, SOF_IPC_STREAM_CAPTURE); + ret = ipc_pcm_params(swidget, stream); if (ret < 0) { dev_err(sdev->dev, "error: failed to set pcm params for widget %s\n", @@ -166,6 +181,11 @@ static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, swidget->widget->name); break; case SND_SOC_DAPM_POST_PMD: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(sdev->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n"); + return 0; + } + /* stop trigger */ ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); if (ret < 0) @@ -433,164 +453,6 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) } /* - * Standard Kcontrols. - */ - -static int sof_control_load_volume(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_mixer_control *mc = - container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - struct sof_ipc_ctrl_data *cdata; - int tlv[TLV_ITEMS]; - unsigned int i; - int ret; - - /* validate topology data */ - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - - /* init the volume get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(mc->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->min_volume_step = le32_to_cpu(mc->min); - scontrol->max_volume_step = le32_to_cpu(mc->max); - scontrol->num_channels = le32_to_cpu(mc->num_channels); - - /* set cmd for mixer control */ - if (le32_to_cpu(mc->max) == 1) { - scontrol->cmd = SOF_CTRL_CMD_SWITCH; - goto out; - } - - scontrol->cmd = SOF_CTRL_CMD_VOLUME; - - /* extract tlv data */ - if (get_tlv_data(kc->tlv.p, tlv) < 0) { - dev_err(sdev->dev, "error: invalid TLV data\n"); - return -EINVAL; - } - - /* set up volume table */ - ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); - if (ret < 0) { - dev_err(sdev->dev, "error: setting up volume table\n"); - return ret; - } - - /* set default volume values to 0dB in control */ - cdata = scontrol->control_data; - for (i = 0; i < scontrol->num_channels; i++) { - cdata->chanv[i].channel = i; - cdata->chanv[i].value = VOL_ZERO_DB; - } - -out: - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); - - return 0; -} - -static int sof_control_load_enum(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_enum_control *ec = - container_of(hdr, struct snd_soc_tplg_enum_control, hdr); - - /* validate topology data */ - if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - - /* init the enum get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(ec->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->num_channels = le32_to_cpu(ec->num_channels); - - scontrol->cmd = SOF_CTRL_CMD_ENUM; - - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", - scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); - - return 0; -} - -static int sof_control_load_bytes(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_ctrl_data *cdata; - struct snd_soc_tplg_bytes_control *control = - container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); - struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; - int max_size = sbe->max; - - /* init the get/put bytes data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - le32_to_cpu(control->priv.size); - - if (scontrol->size > max_size) { - dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", - scontrol->size, max_size); - return -EINVAL; - } - - scontrol->control_data = kzalloc(max_size, GFP_KERNEL); - cdata = scontrol->control_data; - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->cmd = SOF_CTRL_CMD_BINARY; - - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); - - if (le32_to_cpu(control->priv.size) > 0) { - memcpy(cdata->data, control->priv.data, - le32_to_cpu(control->priv.size)); - - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - return -EINVAL; - } - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, - cdata->data->abi)) { - dev_err(sdev->dev, - "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - return -EINVAL; - } - if (cdata->data->size + sizeof(const struct sof_abi_hdr) != - le32_to_cpu(control->priv.size)) { - dev_err(sdev->dev, - "error: Conflict in bytes vs. priv size.\n"); - return -EINVAL; - } - } - return 0; -} - -/* * Topology Token Parsing. * New tokens should be added to headers and parsing tables below. */ @@ -725,6 +587,16 @@ static const struct sof_topology_token pcm_tokens[] = { offsetof(struct sof_ipc_comp_host, dmac_config), 0}, }; +/* PCM */ +static const struct sof_topology_token stream_tokens[] = { + {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible), 0}, + {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, + SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible), 0}, +}; + /* Generic components */ static const struct sof_topology_token comp_tokens[] = { {SOF_TKN_COMP_PERIOD_SINK_COUNT, @@ -799,6 +671,13 @@ static const struct sof_topology_token dmic_tokens[] = { }; +/* ESAI */ +static const struct sof_topology_token esai_tokens[] = { + {SOF_TKN_IMX_ESAI_MCLK_ID, + SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0}, +}; + /* * DMIC PDM Tokens * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token @@ -840,6 +719,14 @@ static const struct sof_topology_token dmic_pdm_tokens[] = { static const struct sof_topology_token hda_tokens[] = { }; +/* Leds */ +static const struct sof_topology_token led_tokens[] = { + {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_led_control, use_led), 0}, + {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, +}; + static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -1040,6 +927,200 @@ static void sof_dbg_comp_config(struct snd_soc_component *scomp, config->frame_fmt); } +/* + * Standard Kcontrols. + */ + +static int sof_control_load_volume(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_mixer_control *mc = + container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); + struct sof_ipc_ctrl_data *cdata; + int tlv[TLV_ITEMS]; + unsigned int i; + int ret = 0; + + /* validate topology data */ + if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) { + ret = -EINVAL; + goto out; + } + + /* init the volume get/put data */ + scontrol->size = struct_size(scontrol->control_data, chanv, + le32_to_cpu(mc->num_channels)); + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->control_data) { + ret = -ENOMEM; + goto out; + } + + scontrol->comp_id = sdev->next_comp_id; + scontrol->min_volume_step = le32_to_cpu(mc->min); + scontrol->max_volume_step = le32_to_cpu(mc->max); + scontrol->num_channels = le32_to_cpu(mc->num_channels); + + /* set cmd for mixer control */ + if (le32_to_cpu(mc->max) == 1) { + scontrol->cmd = SOF_CTRL_CMD_SWITCH; + goto skip; + } + + scontrol->cmd = SOF_CTRL_CMD_VOLUME; + + /* extract tlv data */ + if (get_tlv_data(kc->tlv.p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + ret = -EINVAL; + goto out_free; + } + + /* set up volume table */ + ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); + if (ret < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + goto out_free; + } + + /* set default volume values to 0dB in control */ + cdata = scontrol->control_data; + for (i = 0; i < scontrol->num_channels; i++) { + cdata->chanv[i].channel = i; + cdata->chanv[i].value = VOL_ZERO_DB; + } + +skip: + /* set up possible led control from mixer private data */ + ret = sof_parse_tokens(scomp, &scontrol->led_ctl, led_tokens, + ARRAY_SIZE(led_tokens), mc->priv.array, + le32_to_cpu(mc->priv.size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse led tokens failed %d\n", + le32_to_cpu(mc->priv.size)); + goto out_free_table; + } + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return ret; + +out_free_table: + if (le32_to_cpu(mc->max) > 1) + kfree(scontrol->volume_table); +out_free: + kfree(scontrol->control_data); +out: + return ret; +} + +static int sof_control_load_enum(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_enum_control *ec = + container_of(hdr, struct snd_soc_tplg_enum_control, hdr); + + /* validate topology data */ + if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the enum get/put data */ + scontrol->size = struct_size(scontrol->control_data, chanv, + le32_to_cpu(ec->num_channels)); + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->num_channels = le32_to_cpu(ec->num_channels); + + scontrol->cmd = SOF_CTRL_CMD_ENUM; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", + scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); + + return 0; +} + +static int sof_control_load_bytes(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_ctrl_data *cdata; + struct snd_soc_tplg_bytes_control *control = + container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); + struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; + int max_size = sbe->max; + int ret = 0; + + /* init the get/put bytes data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + le32_to_cpu(control->priv.size); + + if (scontrol->size > max_size) { + dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", + scontrol->size, max_size); + ret = -EINVAL; + goto out; + } + + scontrol->control_data = kzalloc(max_size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) { + ret = -ENOMEM; + goto out; + } + + scontrol->comp_id = sdev->next_comp_id; + scontrol->cmd = SOF_CTRL_CMD_BINARY; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + if (le32_to_cpu(control->priv.size) > 0) { + memcpy(cdata->data, control->priv.data, + le32_to_cpu(control->priv.size)); + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n", + cdata->data->magic); + ret = -EINVAL; + goto out_free; + } + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, + cdata->data->abi)) { + dev_err(sdev->dev, + "error: Incompatible ABI version 0x%08x.\n", + cdata->data->abi); + ret = -EINVAL; + goto out_free; + } + if (cdata->data->size + sizeof(const struct sof_abi_hdr) != + le32_to_cpu(control->priv.size)) { + dev_err(sdev->dev, + "error: Conflict in bytes vs. priv size.\n"); + ret = -EINVAL; + goto out_free; + } + } + + return ret; + +out_free: + kfree(scontrol->control_data); +out: + return ret; +} + /* external kcontrol init - used for any driver specific init */ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_kcontrol_new *kc, @@ -1095,6 +1176,11 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, return 0; } + if (ret < 0) { + kfree(scontrol); + return ret; + } + dobj->private = scontrol; list_add(&scontrol->list, &sdev->kcontrol_list); return ret; @@ -1581,7 +1667,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, if (!volume) return -ENOMEM; - if (le32_to_cpu(tw->num_kcontrols) != 1) { + if (!le32_to_cpu(tw->num_kcontrols)) { dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); ret = -EINVAL; @@ -1618,7 +1704,8 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, swidget->private = volume; list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - if (scontrol->comp_id == swidget->comp_id) { + if (scontrol->comp_id == swidget->comp_id && + scontrol->volume_table) { min_step = scontrol->min_volume_step; max_step = scontrol->max_volume_step; volume->min_value = scontrol->volume_table[min_step]; @@ -2272,6 +2359,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_stream_caps *caps; + struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; int stream = SNDRV_PCM_STREAM_PLAYBACK; int ret = 0; @@ -2288,17 +2376,28 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED; spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED; - if (pcm) { - spcm->pcm = *pcm; - dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); - } + spcm->pcm = *pcm; + dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name); + dai_drv->dobj.private = spcm; list_add(&spcm->list, &sdev->pcm_list); + ret = sof_parse_tokens(scomp, spcm, stream_tokens, + ARRAY_SIZE(stream_tokens), private->array, + le32_to_cpu(private->size)); + if (ret) { + dev_err(sdev->dev, "error: parse stream tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + /* do we need to allocate playback PCM DMA pages */ if (!spcm->pcm.playback) goto capture; + dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n", + spcm->pcm.pcm_name, spcm->stream[0].d0i3_compatible); + caps = &spcm->pcm.caps[stream]; /* allocate playback page table buffer */ @@ -2326,6 +2425,9 @@ capture: if (!spcm->pcm.capture) return ret; + dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n", + spcm->pcm.pcm_name, spcm->stream[1].d0i3_compatible); + caps = &spcm->pcm.caps[stream]; /* allocate capture page table buffer */ @@ -2536,8 +2638,66 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_hw_config *hw_config, struct sof_ipc_dai_config *config) { - /*TODO: Add implementation */ - return 0; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct sof_ipc_reply reply; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params)); + config->hdr.size = size; + + ret = sof_parse_tokens(scomp, &config->esai, esai_tokens, + ARRAY_SIZE(esai_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse esai tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->esai.mclk_direction = hw_config->mclk_direction; + config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(sdev->dev, + "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->esai.mclk_rate, config->esai.tdm_slot_width, + config->esai.tdm_slots, config->esai.mclk_id); + + if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { + dev_err(sdev->dev, "error: invalid channel count for ESAI%d\n", + config->dai_index); + return -EINVAL; + } + + /* send message to DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + config->hdr.cmd, config, size, &reply, + sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to set DAI config for ESAI%d\n", + config->dai_index); + return ret; + } + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(sdev->dev, "error: failed to save DAI config for ESAI%d\n", + config->dai_index); + + return ret; } static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, @@ -2828,6 +2988,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, if (!link->no_pcm) { link->nonatomic = true; + /* set trigger order */ + link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST; + link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST; + /* nothing more to do for FE dai links */ return 0; } diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 4c3cff031fd6..b0e4556c8536 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -162,6 +162,9 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) struct sof_ipc_reply ipc_reply; int ret; + if (!sdev->dtrace_is_supported) + return 0; + if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages) return -EINVAL; @@ -222,6 +225,9 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) { int ret; + if (!sdev->dtrace_is_supported) + return 0; + /* set false before start initialization */ sdev->dtrace_is_enabled = false; @@ -277,6 +283,9 @@ EXPORT_SYMBOL(snd_sof_init_trace); int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn) { + if (!sdev->dtrace_is_supported) + return 0; + if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { sdev->host_offset = posn->host_offset; wake_up(&sdev->trace_sleep); @@ -293,6 +302,9 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, /* an error has occurred within the DSP that prevents further trace */ void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) { + if (!sdev->dtrace_is_supported) + return; + if (sdev->dtrace_is_enabled) { dev_err(sdev->dev, "error: waking up any trace sleepers\n"); sdev->dtrace_error = true; @@ -305,7 +317,7 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) { int ret; - if (!sdev->dtrace_is_enabled) + if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) return; ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); @@ -326,6 +338,9 @@ EXPORT_SYMBOL(snd_sof_release_trace); void snd_sof_free_trace(struct snd_sof_dev *sdev) { + if (!sdev->dtrace_is_supported) + return; + snd_sof_release_trace(sdev); snd_dma_free_pages(&sdev->dmatb); diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c index d38ebbbbf169..da4b8f5f192b 100644 --- a/sound/soc/sprd/sprd-pcm-dma.c +++ b/sound/soc/sprd/sprd-pcm-dma.c @@ -46,12 +46,10 @@ static const struct snd_pcm_hardware sprd_pcm_hardware = { .buffer_bytes_max = 64 * 1024, }; -static int sprd_pcm_open(struct snd_pcm_substream *substream) +static int sprd_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; struct sprd_pcm_dma_private *dma_private; int hw_chan = SPRD_PCM_CHANNEL_MAX; @@ -111,13 +109,11 @@ error: return ret; } -static int sprd_pcm_close(struct snd_pcm_substream *substream) +static int sprd_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sprd_pcm_dma_private *dma_private = runtime->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; int size = runtime->hw.periods_max * SPRD_PCM_DMA_LINKLIST_SIZE; int i; @@ -157,14 +153,12 @@ static void sprd_pcm_release_dma_channel(struct snd_pcm_substream *substream) } } -static int sprd_pcm_request_dma_channel(struct snd_pcm_substream *substream, +static int sprd_pcm_request_dma_channel(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channels) { struct snd_pcm_runtime *runtime = substream->runtime; struct sprd_pcm_dma_private *dma_private = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct device *dev = component->dev; struct sprd_pcm_dma_params *dma_params = dma_private->params; int i; @@ -190,14 +184,13 @@ static int sprd_pcm_request_dma_channel(struct snd_pcm_substream *substream, return 0; } -static int sprd_pcm_hw_params(struct snd_pcm_substream *substream, +static int sprd_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct sprd_pcm_dma_private *dma_private = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct sprd_pcm_dma_params *dma_params; size_t totsize = params_buffer_bytes(params); size_t period = params_period_bytes(params); @@ -218,7 +211,8 @@ static int sprd_pcm_hw_params(struct snd_pcm_substream *substream, if (!dma_private->params) { dma_private->params = dma_params; - ret = sprd_pcm_request_dma_channel(substream, channels); + ret = sprd_pcm_request_dma_channel(component, + substream, channels); if (ret) return ret; } @@ -313,7 +307,8 @@ sg_err: return ret; } -static int sprd_pcm_hw_free(struct snd_pcm_substream *substream) +static int sprd_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); sprd_pcm_release_dma_channel(substream); @@ -321,13 +316,11 @@ static int sprd_pcm_hw_free(struct snd_pcm_substream *substream) return 0; } -static int sprd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int sprd_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct sprd_pcm_dma_private *dma_private = substream->runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); int ret = 0, i; switch (cmd) { @@ -387,13 +380,11 @@ static int sprd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sprd_pcm_dma_private *dma_private = runtime->private_data; - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); int pointer[SPRD_PCM_CHANNEL_MAX]; int bytes_of_pointer = 0, sel_max = 0, i; snd_pcm_uframes_t x; @@ -444,7 +435,8 @@ static snd_pcm_uframes_t sprd_pcm_pointer(struct snd_pcm_substream *substream) return x; } -static int sprd_pcm_mmap(struct snd_pcm_substream *substream, +static int sprd_pcm_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -456,18 +448,8 @@ static int sprd_pcm_mmap(struct snd_pcm_substream *substream, vma->vm_page_prot); } -static struct snd_pcm_ops sprd_pcm_ops = { - .open = sprd_pcm_open, - .close = sprd_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = sprd_pcm_hw_params, - .hw_free = sprd_pcm_hw_free, - .trigger = sprd_pcm_trigger, - .pointer = sprd_pcm_pointer, - .mmap = sprd_pcm_mmap, -}; - -static int sprd_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int sprd_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; @@ -506,7 +488,8 @@ static int sprd_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void sprd_pcm_free(struct snd_pcm *pcm) +static void sprd_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; int i; @@ -523,10 +506,17 @@ static void sprd_pcm_free(struct snd_pcm *pcm) static const struct snd_soc_component_driver sprd_soc_component = { .name = DRV_NAME, - .ops = &sprd_pcm_ops, + .open = sprd_pcm_open, + .close = sprd_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = sprd_pcm_hw_params, + .hw_free = sprd_pcm_hw_free, + .trigger = sprd_pcm_trigger, + .pointer = sprd_pcm_pointer, + .mmap = sprd_pcm_mmap, + .pcm_construct = sprd_pcm_new, + .pcm_destruct = sprd_pcm_free, .compr_ops = &sprd_platform_compr_ops, - .pcm_new = sprd_pcm_new, - .pcm_free = sprd_pcm_free, }; static int sprd_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index 3c9a9deec9af..81c407da15c5 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -210,7 +210,8 @@ static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private) return 0; } -static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd) +static int stm32_adfsdm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = @@ -230,7 +231,8 @@ static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } -static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream) +static int stm32_adfsdm_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); @@ -243,7 +245,8 @@ static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream) return ret; } -static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream) +static int stm32_adfsdm_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct stm32_adfsdm_priv *priv = @@ -256,6 +259,7 @@ static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream) } static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -265,7 +269,8 @@ static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer( return bytes_to_frames(substream->runtime, priv->pos); } -static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream, +static int stm32_adfsdm_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -282,23 +287,16 @@ static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream, params_period_size(params)); } -static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream) +static int stm32_adfsdm_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_lib_free_pages(substream); return 0; } -static struct snd_pcm_ops stm32_adfsdm_pcm_ops = { - .open = stm32_adfsdm_pcm_open, - .close = stm32_adfsdm_pcm_close, - .hw_params = stm32_adfsdm_pcm_hw_params, - .hw_free = stm32_adfsdm_pcm_hw_free, - .trigger = stm32_adfsdm_trigger, - .pointer = stm32_adfsdm_pcm_pointer, -}; - -static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; struct stm32_adfsdm_priv *priv = @@ -310,7 +308,8 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm) +static void stm32_adfsdm_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; @@ -320,9 +319,14 @@ static void stm32_adfsdm_pcm_free(struct snd_pcm *pcm) } static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { - .ops = &stm32_adfsdm_pcm_ops, - .pcm_new = stm32_adfsdm_pcm_new, - .pcm_free = stm32_adfsdm_pcm_free, + .open = stm32_adfsdm_pcm_open, + .close = stm32_adfsdm_pcm_close, + .hw_params = stm32_adfsdm_pcm_hw_params, + .hw_free = stm32_adfsdm_pcm_hw_free, + .trigger = stm32_adfsdm_trigger, + .pointer = stm32_adfsdm_pcm_pointer, + .pcm_construct = stm32_adfsdm_pcm_new, + .pcm_destruct = stm32_adfsdm_pcm_free, }; static const struct of_device_id stm32_adfsdm_of_match[] = { diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index ef4273361d0d..e20267504b16 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -100,7 +100,7 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n", sai->pdev->dev.of_node, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); - stm32_sai_pclk_disable(&sai->pdev->dev); + stm32_sai_pclk_disable(&sai->pdev->dev); return -EINVAL; } diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index cd4b235fce57..3fd28ee01675 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -351,6 +351,8 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO; cr_mask = cr; + cr |= SPDIFRX_CR_NBTRSET(SPDIFRX_NBTR_63); + cr_mask |= SPDIFRX_CR_NBTR_MASK; cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); cr_mask |= SPDIFRX_CR_SPDIFEN_MASK; ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, @@ -666,7 +668,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) struct snd_pcm_substream *substream = spdifrx->substream; struct platform_device *pdev = spdifrx->pdev; unsigned int cr, mask, sr, imr; - unsigned int flags; + unsigned int flags, sync_state; int err = 0, err_xrun = 0; regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr); @@ -726,11 +728,23 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) } if (err) { - /* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */ + regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr); + sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) && + SPDIFRX_SPDIFEN_SYNC; + + /* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */ cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE); regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, SPDIFRX_CR_SPDIFEN_MASK, cr); + /* If SPDIFRX was in STATE_SYNC, retry synchro */ + if (sync_state) { + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_SPDIFEN_MASK, cr); + return IRQ_HANDLED; + } + if (substream) snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index ee448d5e07a6..34f3e0be3058 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1442,7 +1442,7 @@ static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev) if (!aux_dev.dlc.of_node) { dev_err(dev, "Can't find analog controls for codec.\n"); return ERR_PTR(-EINVAL); - }; + } card->dai_link = sun4i_codec_create_link(dev, &card->num_links); if (!card->dai_link) @@ -1480,7 +1480,7 @@ static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) if (!aux_dev.dlc.of_node) { dev_err(dev, "Can't find analog controls for codec.\n"); return ERR_PTR(-EINVAL); - }; + } card->dai_link = sun4i_codec_create_link(dev, &card->num_links); if (!card->dai_link) @@ -1518,7 +1518,7 @@ static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev) if (!aux_dev.dlc.of_node) { dev_err(dev, "Can't find analog controls for codec.\n"); return ERR_PTR(-EINVAL); - }; + } card->dai_link = sun4i_codec_create_link(dev, &card->num_links); if (!card->dai_link) diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index e6d548fa980b..dbed3c5408e7 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -127,7 +127,7 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, struct device *dev = dai->dev; struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int mask, val, reg; - int ret, sample_size, srate, i2sclock, bitcnt; + int ret, sample_size, srate, i2sclock, bitcnt, audio_bits; struct tegra30_ahub_cif_conf cif_conf; if (params_channels(params) != 2) @@ -137,8 +137,19 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val = TEGRA30_I2S_CTRL_BIT_SIZE_16; + audio_bits = TEGRA30_AUDIOCIF_BITS_16; sample_size = 16; break; + case SNDRV_PCM_FORMAT_S24_LE: + val = TEGRA30_I2S_CTRL_BIT_SIZE_24; + audio_bits = TEGRA30_AUDIOCIF_BITS_24; + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val = TEGRA30_I2S_CTRL_BIT_SIZE_32; + audio_bits = TEGRA30_AUDIOCIF_BITS_32; + sample_size = 32; + break; default: return -EINVAL; } @@ -170,8 +181,8 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, cif_conf.threshold = 0; cif_conf.audio_channels = 2; cif_conf.client_channels = 2; - cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16; - cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = audio_bits; cif_conf.expand = 0; cif_conf.stereo_conv = 0; cif_conf.replicate = 0; @@ -220,9 +231,9 @@ static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s) static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s) { - tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif); regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, TEGRA30_I2S_CTRL_XFER_EN_RX, 0); + tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif); } static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd, @@ -254,6 +265,34 @@ static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int tegra30_i2s_set_tdm(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int mask, val; + + dev_dbg(dai->dev, "%s: txmask=0x%08x rxmask=0x%08x slots=%d width=%d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + mask = TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK | + TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK | + TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK; + + val = (tx_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT) | + (rx_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) | + ((slots - 1) << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT); + + pm_runtime_get_sync(dai->dev); + regmap_update_bits(i2s->regmap, TEGRA30_I2S_SLOT_CTRL, mask, val); + /* set the fsync width to minimum of 1 clock width */ + regmap_update_bits(i2s->regmap, TEGRA30_I2S_CH_CTRL, + TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK, 0x0); + pm_runtime_put(dai->dev); + + return 0; +} + static int tegra30_i2s_probe(struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -268,6 +307,7 @@ static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { .set_fmt = tegra30_i2s_set_fmt, .hw_params = tegra30_i2s_hw_params, .trigger = tegra30_i2s_trigger, + .set_tdm_slot = tegra30_i2s_set_tdm, }; static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { @@ -277,14 +317,18 @@ static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra30_i2s_dai_ops, .symmetric_rates = 1, diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 7aa3c32e4a49..8e5371801d88 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1867,7 +1867,7 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) return PCM_EDMA; tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; - chan = dma_request_slave_channel_reason(mcasp->dev, tmp); + chan = dma_request_chan(mcasp->dev, tmp); if (IS_ERR(chan)) { if (PTR_ERR(chan) != -EPROBE_DEFER) dev_err(mcasp->dev, diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 66044559f70f..33c78d33e5a1 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -47,12 +47,12 @@ static const struct snd_pcm_hardware txx9aclc_pcm_hardware = { .buffer_bytes_max = 32 * 1024, }; -static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream, +static int txx9aclc_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct txx9aclc_dmadata *dmadata = runtime->private_data; int ret; @@ -76,12 +76,14 @@ static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream) +static int txx9aclc_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream) +static int txx9aclc_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct txx9aclc_dmadata *dmadata = runtime->private_data; @@ -203,7 +205,8 @@ static void txx9aclc_dma_tasklet(unsigned long data) spin_unlock_irqrestore(&dmadata->dma_lock, flags); } -static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int txx9aclc_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata; @@ -236,14 +239,16 @@ static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t -txx9aclc_pcm_pointer(struct snd_pcm_substream *substream) +txx9aclc_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; return bytes_to_frames(substream->runtime, dmadata->pos); } -static int txx9aclc_pcm_open(struct snd_pcm_substream *substream) +static int txx9aclc_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct txx9aclc_soc_device *dev = &txx9aclc_soc_device; struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream]; @@ -261,7 +266,8 @@ static int txx9aclc_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int txx9aclc_pcm_close(struct snd_pcm_substream *substream) +static int txx9aclc_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct txx9aclc_dmadata *dmadata = substream->runtime->private_data; struct dma_chan *chan = dmadata->dma_chan; @@ -271,23 +277,12 @@ static int txx9aclc_pcm_close(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops txx9aclc_pcm_ops = { - .open = txx9aclc_pcm_open, - .close = txx9aclc_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = txx9aclc_pcm_hw_params, - .hw_free = txx9aclc_pcm_hw_free, - .prepare = txx9aclc_pcm_prepare, - .trigger = txx9aclc_pcm_trigger, - .pointer = txx9aclc_pcm_pointer, -}; - -static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int txx9aclc_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct platform_device *pdev = to_platform_device(component->dev); struct txx9aclc_soc_device *dev; struct resource *r; @@ -409,8 +404,15 @@ static const struct snd_soc_component_driver txx9aclc_soc_component = { .name = DRV_NAME, .probe = txx9aclc_pcm_probe, .remove = txx9aclc_pcm_remove, - .ops = &txx9aclc_pcm_ops, - .pcm_new = txx9aclc_pcm_new, + .open = txx9aclc_pcm_open, + .close = txx9aclc_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = txx9aclc_pcm_hw_params, + .hw_free = txx9aclc_pcm_hw_free, + .prepare = txx9aclc_pcm_prepare, + .trigger = txx9aclc_pcm_trigger, + .pointer = txx9aclc_pcm_pointer, + .pcm_construct = txx9aclc_pcm_new, }; static int txx9aclc_soc_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c index e8446cc4e8f8..700d936ed94e 100644 --- a/sound/soc/uniphier/aio-dma.c +++ b/sound/soc/uniphier/aio-dma.c @@ -93,7 +93,8 @@ static irqreturn_t aiodma_irq(int irq, void *p) return ret; } -static int uniphier_aiodma_open(struct snd_pcm_substream *substream) +static int uniphier_aiodma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -103,7 +104,8 @@ static int uniphier_aiodma_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256); } -static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream, +static int uniphier_aiodma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); @@ -112,7 +114,8 @@ static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream, return 0; } -static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream) +static int uniphier_aiodma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { snd_pcm_set_runtime_buffer(substream, NULL); substream->runtime->dma_bytes = 0; @@ -120,7 +123,8 @@ static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream) return 0; } -static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream) +static int uniphier_aiodma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); @@ -146,7 +150,8 @@ static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream) return 0; } -static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd) +static int uniphier_aiodma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); @@ -181,6 +186,7 @@ static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t uniphier_aiodma_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -204,7 +210,8 @@ static snd_pcm_uframes_t uniphier_aiodma_pointer( return pos; } -static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream, +static int uniphier_aiodma_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct vm_area_struct *vma) { vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); @@ -214,18 +221,8 @@ static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream, vma->vm_end - vma->vm_start, vma->vm_page_prot); } -static const struct snd_pcm_ops uniphier_aiodma_ops = { - .open = uniphier_aiodma_open, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = uniphier_aiodma_hw_params, - .hw_free = uniphier_aiodma_hw_free, - .prepare = uniphier_aiodma_prepare, - .trigger = uniphier_aiodma_trigger, - .pointer = uniphier_aiodma_pointer, - .mmap = uniphier_aiodma_mmap, -}; - -static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd) +static int uniphier_aiodma_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct device *dev = rtd->card->snd_card->dev; struct snd_pcm *pcm = rtd->pcm; @@ -242,16 +239,24 @@ static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static void uniphier_aiodma_free(struct snd_pcm *pcm) +static void uniphier_aiodma_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); } static const struct snd_soc_component_driver uniphier_soc_platform = { - .pcm_new = uniphier_aiodma_new, - .pcm_free = uniphier_aiodma_free, - .ops = &uniphier_aiodma_ops, - .compr_ops = &uniphier_aio_compr_ops, + .open = uniphier_aiodma_open, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = uniphier_aiodma_hw_params, + .hw_free = uniphier_aiodma_hw_free, + .prepare = uniphier_aiodma_prepare, + .trigger = uniphier_aiodma_trigger, + .pointer = uniphier_aiodma_pointer, + .mmap = uniphier_aiodma_mmap, + .pcm_construct = uniphier_aiodma_new, + .pcm_destruct = uniphier_aiodma_free, + .compr_ops = &uniphier_aio_compr_ops, }; static const struct regmap_config aiodma_regmap_config = { diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index a90e0d7f0b73..394d8b2a4a16 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -533,7 +533,6 @@ static void disable_msp_tx(struct ux500_msp *msp) static int disable_msp(struct ux500_msp *msp, unsigned int dir) { u32 reg_val_GCR; - int status = 0; unsigned int disable_tx, disable_rx; reg_val_GCR = readl(msp->registers + MSP_GCR); @@ -566,7 +565,7 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir) else if (disable_rx) disable_msp_rx(msp); - return status; + return 0; } int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 48970efe7838..296c4caf96a0 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -313,16 +313,14 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) return IRQ_NONE; } -static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream) +static int xlnx_formatter_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { int err; u32 val, data_format_mode; u32 ch_count_mask, ch_count_shift, data_xfer_mode, data_xfer_shift; struct xlnx_pcm_stream_param *stream_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct xlnx_pcm_drv_data *adata = dev_get_drvdata(component->dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && @@ -387,14 +385,12 @@ static int xlnx_formatter_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int xlnx_formatter_pcm_close(struct snd_pcm_substream *substream) +static int xlnx_formatter_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { int ret; struct xlnx_pcm_stream_param *stream_data = substream->runtime->private_data; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); ret = xlnx_formatter_pcm_reset(stream_data->mmio); if (ret) { @@ -409,7 +405,8 @@ err_reset: } static snd_pcm_uframes_t -xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream) +xlnx_formatter_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { u32 pos; struct snd_pcm_runtime *runtime = substream->runtime; @@ -423,16 +420,14 @@ xlnx_formatter_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, pos); } -static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream, +static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { u32 low, high, active_ch, val, bytes_per_ch, bits_per_sample; u32 aes_reg1_val, aes_reg2_val; int status; u64 size; - struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, - DRV_NAME); struct snd_pcm_runtime *runtime = substream->runtime; struct xlnx_pcm_stream_param *stream_data = runtime->private_data; @@ -500,12 +495,14 @@ static int xlnx_formatter_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int xlnx_formatter_pcm_hw_free(struct snd_pcm_substream *substream) +static int xlnx_formatter_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream, +static int xlnx_formatter_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { u32 val; @@ -532,10 +529,9 @@ static int xlnx_formatter_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int xlnx_formatter_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, - DRV_NAME); snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, xlnx_pcm_hardware.buffer_bytes_max, @@ -543,20 +539,16 @@ static int xlnx_formatter_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static const struct snd_pcm_ops xlnx_formatter_pcm_ops = { - .open = xlnx_formatter_pcm_open, - .close = xlnx_formatter_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = xlnx_formatter_pcm_hw_params, - .hw_free = xlnx_formatter_pcm_hw_free, - .trigger = xlnx_formatter_pcm_trigger, - .pointer = xlnx_formatter_pcm_pointer, -}; - static const struct snd_soc_component_driver xlnx_asoc_component = { - .name = DRV_NAME, - .ops = &xlnx_formatter_pcm_ops, - .pcm_new = xlnx_formatter_pcm_new, + .name = DRV_NAME, + .open = xlnx_formatter_pcm_open, + .close = xlnx_formatter_pcm_close, + .ioctl = snd_soc_pcm_lib_ioctl, + .hw_params = xlnx_formatter_pcm_hw_params, + .hw_free = xlnx_formatter_pcm_hw_free, + .trigger = xlnx_formatter_pcm_trigger, + .pointer = xlnx_formatter_pcm_pointer, + .pcm_construct = xlnx_formatter_pcm_new, }; static int xlnx_formatter_pcm_probe(struct platform_device *pdev) @@ -564,7 +556,6 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) int ret; u32 val; struct xlnx_pcm_drv_data *aud_drv_data; - struct resource *res; struct device *dev = &pdev->dev; aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL); @@ -584,13 +575,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) return ret; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "audio formatter node:addr to resource failed\n"); - ret = -ENXIO; - goto clk_err; - } - aud_drv_data->mmio = devm_ioremap_resource(dev, res); + aud_drv_data->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(aud_drv_data->mmio)) { dev_err(dev, "audio formatter ioremap failed\n"); ret = PTR_ERR(aud_drv_data->mmio); diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index efd374f114a0..e08f4fee932a 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -365,7 +365,8 @@ static const struct snd_pcm_hardware xtfpga_pcm_hardware = { .fifo_size = 16, }; -static int xtfpga_pcm_open(struct snd_pcm_substream *substream) +static int xtfpga_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -378,13 +379,15 @@ static int xtfpga_pcm_open(struct snd_pcm_substream *substream) return 0; } -static int xtfpga_pcm_close(struct snd_pcm_substream *substream) +static int xtfpga_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { synchronize_rcu(); return 0; } -static int xtfpga_pcm_hw_params(struct snd_pcm_substream *substream, +static int xtfpga_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { int ret; @@ -424,7 +427,8 @@ static int xtfpga_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } -static int xtfpga_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int xtfpga_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) { int ret = 0; struct snd_pcm_runtime *runtime = substream->runtime; @@ -452,7 +456,8 @@ static int xtfpga_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct xtfpga_i2s *i2s = runtime->private_data; @@ -461,7 +466,8 @@ static snd_pcm_uframes_t xtfpga_pcm_pointer(struct snd_pcm_substream *substream) return pos < runtime->buffer_size ? pos : 0; } -static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int xtfpga_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; size_t size = xtfpga_pcm_hardware.buffer_bytes_max; @@ -471,19 +477,15 @@ static int xtfpga_pcm_new(struct snd_soc_pcm_runtime *rtd) return 0; } -static const struct snd_pcm_ops xtfpga_pcm_ops = { +static const struct snd_soc_component_driver xtfpga_i2s_component = { + .name = DRV_NAME, .open = xtfpga_pcm_open, .close = xtfpga_pcm_close, - .ioctl = snd_pcm_lib_ioctl, + .ioctl = snd_soc_pcm_lib_ioctl, .hw_params = xtfpga_pcm_hw_params, .trigger = xtfpga_pcm_trigger, .pointer = xtfpga_pcm_pointer, -}; - -static const struct snd_soc_component_driver xtfpga_i2s_component = { - .name = DRV_NAME, - .pcm_new = xtfpga_pcm_new, - .ops = &xtfpga_pcm_ops, + .pcm_construct = xtfpga_pcm_new, }; static const struct snd_soc_dai_ops xtfpga_i2s_dai_ops = { |