diff options
344 files changed, 16592 insertions, 5305 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-avs b/Documentation/ABI/testing/sysfs-bus-pci-devices-avs new file mode 100644 index 000000000000..ebff3fa12055 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-avs @@ -0,0 +1,8 @@ +What: /sys/devices/pci0000:00/<dev>/avs/fw_version +Date: February 2024 +Contact: Cezary Rojewski <cezary.rojewski@intel.com> +Description: + Version of AudioDSP firmware ASoC avs driver is communicating + with. + + Format: %d.%d.%d.%d, type:major:minor:build. diff --git a/Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml b/Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml new file mode 100644 index 000000000000..89a67f8e33b6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel,asoc-wm8904.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/atmel,asoc-wm8904.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel wm8904 audio codec complex + +maintainers: + - Dharma Balasubiramani <dharma.b@microchip.com> + +description: + The ASoC audio complex configuration for Atmel with WM8904 audio codec. + +properties: + compatible: + const: atmel,asoc-wm8904 + + atmel,model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + atmel,ssc-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the SSC controller. + + atmel,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the WM8731 audio codec. + + atmel,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. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + items: + enum: + # Board Connectors + - Headphone Jack + - Line In Jack + - Mic + # WM8904 CODEC Pins + - IN1L + - IN1R + - IN2L + - IN2R + - IN3L + - IN3R + - HPOUTL + - HPOUTR + - LINEOUTL + - LINEOUTR + - MICBIAS + +required: + - compatible + - atmel,model + - atmel,audio-routing + - atmel,ssc-controller + - atmel,audio-codec + +additionalProperties: false + +examples: + - | + sound { + compatible = "atmel,asoc-wm8904"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_mck>; + + atmel,model = "wm8904 @ AT91SAM9N12EK"; + + atmel,audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "Mic", "MICBIAS", + "IN1L", "Mic"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8904>; + }; diff --git a/Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml b/Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml new file mode 100644 index 000000000000..33717b728f63 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel,sam9x5-wm8731-audio.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/atmel,sam9x5-wm8731-audio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel at91sam9x5ek wm8731 audio complex + +maintainers: + - Dharma Balasubiramani <dharma.b@microchip.com> + +description: + The audio complex configuration for Atmel at91sam9x5ek with WM8731 audio codec. + +properties: + compatible: + const: atmel,sam9x5-wm8731-audio + + atmel,model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex. + + atmel,ssc-controller: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the SSC controller. + + atmel,audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the WM8731 audio codec. + + atmel,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. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + items: + enum: + # Board Connectors + - Headphone Jack + - Line In Jack + + # CODEC Pins + - LOUT + - ROUT + - LHPOUT + - RHPOUT + - LLINEIN + - RLINEIN + - MICIN + +required: + - compatible + - atmel,model + - atmel,ssc-controller + - atmel,audio-codec + - atmel,audio-routing + +additionalProperties: false + +examples: + - | + sound { + compatible = "atmel,sam9x5-wm8731-audio"; + + atmel,model = "wm8731 @ AT91SAM9X5EK"; + + atmel,audio-routing = + "Headphone Jack", "RHPOUT", + "Headphone Jack", "LHPOUT", + "LLINEIN", "Line In Jack", + "RLINEIN", "Line In Jack"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8731>; + }; diff --git a/Documentation/devicetree/bindings/sound/atmel,sama5d2-classd.yaml b/Documentation/devicetree/bindings/sound/atmel,sama5d2-classd.yaml index 43d04702ac2d..ae3162fcfe02 100644 --- a/Documentation/devicetree/bindings/sound/atmel,sama5d2-classd.yaml +++ b/Documentation/devicetree/bindings/sound/atmel,sama5d2-classd.yaml @@ -18,7 +18,12 @@ description: properties: compatible: - const: atmel,sama5d2-classd + oneOf: + - items: + - const: atmel,sama5d2-classd + - items: + - const: microchip,sam9x7-classd + - const: atmel,sama5d2-classd reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt deleted file mode 100644 index 8facbce53db8..000000000000 --- a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Atmel at91sam9x5ek wm8731 audio complex - -Required properties: - - compatible: "atmel,sam9x5-wm8731-audio" - - atmel,model: The user-visible name of this sound complex. - - atmel,ssc-controller: The phandle of the SSC controller - - atmel,audio-codec: The phandle of the WM8731 audio codec - - atmel,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. - -Available audio endpoints for the audio-routing table: - -Board connectors: - * Headphone Jack - * Line In Jack - -wm8731 pins: -cf Documentation/devicetree/bindings/sound/wlf,wm8731.yaml - -Example: -sound { - compatible = "atmel,sam9x5-wm8731-audio"; - - atmel,model = "wm8731 @ AT91SAM9X5EK"; - - atmel,audio-routing = - "Headphone Jack", "RHPOUT", - "Headphone Jack", "LHPOUT", - "LLINEIN", "Line In Jack", - "RLINEIN", "Line In Jack"; - - atmel,ssc-controller = <&ssc0>; - atmel,audio-codec = <&wm8731>; -}; diff --git a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt deleted file mode 100644 index 8bbe50c884b6..000000000000 --- a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt +++ /dev/null @@ -1,55 +0,0 @@ -Atmel ASoC driver with wm8904 audio codec complex - -Required properties: - - compatible: "atmel,asoc-wm8904" - - atmel,model: The user-visible name of this sound complex. - - atmel,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 WM8904's pins, and the jacks on the board: - - WM8904 pins: - - * IN1L - * IN1R - * IN2L - * IN2R - * IN3L - * IN3R - * HPOUTL - * HPOUTR - * LINEOUTL - * LINEOUTR - * MICBIAS - - Board connectors: - - * Headphone Jack - * Line In Jack - * Mic - - - atmel,ssc-controller: The phandle of the SSC controller - - atmel,audio-codec: The phandle of the WM8904 audio codec - -Optional properties: - - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt - -Example: -sound { - compatible = "atmel,asoc-wm8904"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pck0_as_mck>; - - atmel,model = "wm8904 @ AT91SAM9N12EK"; - - atmel,audio-routing = - "Headphone Jack", "HPOUTL", - "Headphone Jack", "HPOUTR", - "IN2L", "Line In Jack", - "IN2R", "Line In Jack", - "Mic", "MICBIAS", - "IN1L", "Mic"; - - atmel,ssc-controller = <&ssc0>; - atmel,audio-codec = <&wm8904>; -}; diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index b13c08de505e..28b27e7e45de 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -51,7 +51,7 @@ definitions: - $ref: /schemas/types.yaml#/definitions/phandle clocks: description: Indicates system clock - $ref: /schemas/types.yaml#/definitions/phandle + maxItems: 1 system-clock-frequency: $ref: simple-card.yaml#/definitions/system-clock-frequency system-clock-direction-out: diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml index 4c9acb8d4c4c..70f6c62aedca 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml @@ -25,6 +25,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + '#sound-dai-cells': const: 1 diff --git a/Documentation/devicetree/bindings/sound/everest,es8326.yaml b/Documentation/devicetree/bindings/sound/everest,es8326.yaml index 07781408e788..8c82d47375ec 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8326.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8326.yaml @@ -38,6 +38,7 @@ properties: default: 0x0f everest,mic1-src: + deprecated: true $ref: /schemas/types.yaml#/definitions/uint8 description: the value of reg 2A when headset plugged. @@ -46,6 +47,7 @@ properties: default: 0x22 everest,mic2-src: + deprecated: true $ref: /schemas/types.yaml#/definitions/uint8 description: the value of reg 2A when headset unplugged. @@ -87,7 +89,7 @@ properties: 0 means the chip detect jack type again after button released. minimum: 0 maximum: 0x7f - default: 0x45 + default: 0x00 required: - compatible @@ -107,10 +109,8 @@ examples: clocks = <&clks 10>; clock-names = "mclk"; #sound-dai-cells = <0>; - everest,mic1-src = [22]; - everest,mic2-src = [44]; everest,jack-pol = [0e]; everest,interrupt-src = [08]; - everest,interrupt-clk = [45]; + everest,interrupt-clk = [00]; }; }; diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt deleted file mode 100644 index 998b4c8a7f78..000000000000 --- a/Documentation/devicetree/bindings/sound/fsl,asrc.txt +++ /dev/null @@ -1,80 +0,0 @@ -Freescale Asynchronous Sample Rate Converter (ASRC) Controller - -The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a -signal associated with an input clock into a signal associated with a different -output clock. The driver currently works as a Front End of DPCM with other Back -Ends Audio controller such as ESAI, SSI and SAI. It has three pairs to support -three substreams within totally 10 channels. - -Required properties: - - - compatible : Compatible list, should contain one of the following - compatibles: - "fsl,imx35-asrc", - "fsl,imx53-asrc", - "fsl,imx8qm-asrc", - "fsl,imx8qxp-asrc", - - - reg : Offset and length of the register set for the device. - - - interrupts : Contains the spdif interrupt. - - - dmas : Generic dma devicetree binding as described in - Documentation/devicetree/bindings/dma/dma.txt. - - - dma-names : Contains "rxa", "rxb", "rxc", "txa", "txb" and "txc". - - - clocks : Contains an entry for each entry in clock-names. - - - clock-names : Contains the following entries - "mem" Peripheral access clock to access registers. - "ipg" Peripheral clock to driver module. - "asrck_<0-f>" Clock sources for input and output clock. - "spba" The spba clock is required when ASRC is placed as a - bus slave of the Shared Peripheral Bus and when two - or more bus masters (CPU, DMA or DSP) try to access - it. This property is optional depending on the SoC - design. - - - fsl,asrc-rate : Defines a mutual sample rate used by DPCM Back Ends. - - - fsl,asrc-width : Defines a mutual sample width used by DPCM Back Ends. - - - fsl,asrc-clk-map : Defines clock map used in driver. which is required - by imx8qm/imx8qxp platform - <0> - select the map for asrc0 in imx8qm/imx8qxp - <1> - select the map for asrc1 in imx8qm/imx8qxp - -Optional properties: - - - big-endian : If this property is absent, the little endian mode - will be in use as default. Otherwise, the big endian - mode will be in use for all the device registers. - - - fsl,asrc-format : Defines a mutual sample format used by DPCM Back - Ends, which can replace the fsl,asrc-width. - The value is 2 (S16_LE), or 6 (S24_LE). - -Example: - -asrc: asrc@2034000 { - compatible = "fsl,imx53-asrc"; - reg = <0x02034000 0x4000>; - interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks 107>, <&clks 107>, <&clks 0>, - <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, - <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, - <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, - <&clks 107>, <&clks 0>, <&clks 0>; - clock-names = "mem", "ipg", "asrck0", - "asrck_1", "asrck_2", "asrck_3", "asrck_4", - "asrck_5", "asrck_6", "asrck_7", "asrck_8", - "asrck_9", "asrck_a", "asrck_b", "asrck_c", - "asrck_d", "asrck_e", "asrck_f"; - dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>, - <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>; - dma-names = "rxa", "rxb", "rxc", - "txa", "txb", "txc"; - fsl,asrc-rate = <48000>; - fsl,asrc-width = <16>; -}; diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml index a680d7aff237..0782f3f9947f 100644 --- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml @@ -51,8 +51,8 @@ properties: - const: ctx3_tx firmware-name: - $ref: /schemas/types.yaml#/definitions/string - const: imx/easrc/easrc-imx8mn.bin + items: + - const: imx/easrc/easrc-imx8mn.bin description: The coefficient table for the filters fsl,asrc-rate: diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml new file mode 100644 index 000000000000..bfef2fcb75b1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/fsl,imx-asrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Asynchronous Sample Rate Converter (ASRC) Controller + +description: + The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of + a signal associated with an input clock into a signal associated with a + different output clock. The driver currently works as a Front End of DPCM + with other Back Ends Audio controller such as ESAI, SSI and SAI. It has + three pairs to support three substreams within totally 10 channels. + +maintainers: + - Shawn Guo <shawnguo@kernel.org> + - Sascha Hauer <s.hauer@pengutronix.de> + +properties: + compatible: + oneOf: + - enum: + - fsl,imx35-asrc + - fsl,imx53-asrc + - fsl,imx8qm-asrc + - fsl,imx8qxp-asrc + - items: + - enum: + - fsl,imx6sx-asrc + - fsl,imx6ul-asrc + - const: fsl,imx53-asrc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + maxItems: 6 + + dma-names: + items: + - const: rxa + - const: rxb + - const: rxc + - const: txa + - const: txb + - const: txc + + clocks: + maxItems: 19 + + clock-names: + items: + - const: mem + - const: ipg + - const: asrck_0 + - const: asrck_1 + - const: asrck_2 + - const: asrck_3 + - const: asrck_4 + - const: asrck_5 + - const: asrck_6 + - const: asrck_7 + - const: asrck_8 + - const: asrck_9 + - const: asrck_a + - const: asrck_b + - const: asrck_c + - const: asrck_d + - const: asrck_e + - const: asrck_f + - const: spba + + fsl,asrc-rate: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The mutual sample rate used by DPCM Back Ends + + fsl,asrc-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The mutual sample width used by DPCM Back Ends + enum: [16, 24] + + fsl,asrc-clk-map: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Defines clock map used in driver + <0> - select the map for asrc0 in imx8qm/imx8qxp + <1> - select the map for asrc1 in imx8qm/imx8qxp + enum: [0, 1] + + big-endian: + type: boolean + description: + If this property is absent, the little endian mode will be in use as + default. Otherwise, the big endian mode will be in use for all the + device registers. + + fsl,asrc-format: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Defines a mutual sample format used by DPCM Back Ends, which can + replace the fsl,asrc-width. The value is 2 (S16_LE), or 6 (S24_LE). + enum: [2, 6] + +required: + - compatible + - reg + - interrupts + - dmas + - dma-names + - clocks + - clock-names + - fsl,asrc-rate + - fsl,asrc-width + +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-asrc + - fsl,imx8qxp-asrc + then: + required: + - fsl,asrc-clk-map + else: + properties: + fsl,asrc-clk-map: false + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/clock/imx6qdl-clock.h> + asrc: asrc@2034000 { + compatible = "fsl,imx53-asrc"; + reg = <0x02034000 0x4000>; + interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX6QDL_CLK_ASRC_IPG>, + <&clks IMX6QDL_CLK_ASRC_MEM>, <&clks 0>, + <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, + <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, + <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>, + <&clks IMX6QDL_CLK_ASRC>, <&clks 0>, <&clks 0>, + <&clks IMX6QDL_CLK_SPBA>; + clock-names = "mem", "ipg", "asrck_0", + "asrck_1", "asrck_2", "asrck_3", "asrck_4", + "asrck_5", "asrck_6", "asrck_7", "asrck_8", + "asrck_9", "asrck_a", "asrck_b", "asrck_c", + "asrck_d", "asrck_e", "asrck_f", "spba"; + dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>, + <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>; + dma-names = "rxa", "rxb", "rxc", + "txa", "txb", "txc"; + fsl,asrc-rate = <48000>; + fsl,asrc-width = <16>; + }; diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml index b7e605835639..c1e9803fc113 100644 --- a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml @@ -15,10 +15,16 @@ description: | properties: compatible: - enum: - - fsl,imx8mm-micfil - - fsl,imx8mp-micfil - - fsl,imx93-micfil + oneOf: + - items: + - enum: + - fsl,imx95-micfil + - const: fsl,imx93-micfil + + - enum: + - fsl,imx8mm-micfil + - fsl,imx8mp-micfil + - fsl,imx93-micfil reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml index 088c26b001cc..2456d958adee 100644 --- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml @@ -39,6 +39,7 @@ properties: - fsl,imx8qm-sai - fsl,imx8ulp-sai - fsl,imx93-sai + - fsl,imx95-sai - fsl,vf610-sai reg: @@ -75,12 +76,17 @@ properties: - const: pll11k minItems: 4 + power-domains: + maxItems: 1 + dmas: + minItems: 1 items: - description: DMA controller phandle and request line for RX - description: DMA controller phandle and request line for TX dma-names: + minItems: 1 items: - const: rx - const: tx diff --git a/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml index 66993d378aaf..5e11ce2c13ac 100644 --- a/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml +++ b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml @@ -51,7 +51,7 @@ properties: maxItems: 1 firmware-name: - $ref: /schemas/types.yaml#/definitions/string + maxItems: 1 description: Filters coefficients file to load. If this property is omitted, internal filters are disabled. diff --git a/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml index 651f61c7c25a..fb630a184350 100644 --- a/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml +++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml @@ -24,9 +24,14 @@ properties: const: 0 compatible: - enum: - - microchip,sam9x60-i2smcc - - microchip,sama7g5-i2smcc + oneOf: + - enum: + - microchip,sam9x60-i2smcc + - microchip,sama7g5-i2smcc + - items: + - enum: + - microchip,sam9x7-i2smcc + - const: microchip,sam9x60-i2smcc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 6f419747273e..2ab6871e89e5 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -107,7 +107,7 @@ patternProperties: properties: sound-dai: minItems: 1 - maxItems: 4 + maxItems: 8 required: - link-name diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml index adbfa67f88ed..cf6c3787adfe 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml @@ -15,6 +15,7 @@ description: | allOf: - $ref: dai-common.yaml# + - $ref: qcom,wcd93xx-common.yaml# properties: compatible: @@ -22,92 +23,12 @@ properties: - qcom,wcd9380-codec - qcom,wcd9385-codec - reset-gpios: - description: GPIO spec for reset line to use - maxItems: 1 - us-euro-gpios: description: GPIO spec for swapping gnd and mic segments maxItems: 1 - vdd-buck-supply: - description: A reference to the 1.8V buck supply - - vdd-rxtx-supply: - description: A reference to the 1.8V rx supply - - vdd-io-supply: - description: A reference to the 1.8V I/O supply - - vdd-mic-bias-supply: - description: A reference to the 3.8V mic bias supply - - qcom,tx-device: - $ref: /schemas/types.yaml#/definitions/phandle-array - description: A reference to Soundwire tx device phandle - - qcom,rx-device: - $ref: /schemas/types.yaml#/definitions/phandle-array - description: A reference to Soundwire rx device phandle - - qcom,micbias1-microvolt: - description: micbias1 voltage - minimum: 1800000 - maximum: 2850000 - - qcom,micbias2-microvolt: - description: micbias2 voltage - minimum: 1800000 - maximum: 2850000 - - qcom,micbias3-microvolt: - description: micbias3 voltage - minimum: 1800000 - maximum: 2850000 - - qcom,micbias4-microvolt: - description: micbias4 voltage - minimum: 1800000 - maximum: 2850000 - - qcom,hphl-jack-type-normally-closed: - description: Indicates that HPHL jack switch type is normally closed - type: boolean - - qcom,ground-jack-type-normally-closed: - description: Indicates that Headset Ground switch type is normally closed - type: boolean - - qcom,mbhc-headset-vthreshold-microvolt: - description: Voltage threshold value for headset detection - minimum: 0 - maximum: 2850000 - - qcom,mbhc-headphone-vthreshold-microvolt: - description: Voltage threshold value for headphone detection - minimum: 0 - maximum: 2850000 - - qcom,mbhc-buttons-vthreshold-microvolt: - description: - Array of 8 Voltage threshold values corresponding to headset - button0 - button7 - minItems: 8 - maxItems: 8 - - '#sound-dai-cells': - const: 1 - required: - compatible - - reset-gpios - - qcom,tx-device - - qcom,rx-device - - qcom,micbias1-microvolt - - qcom,micbias2-microvolt - - qcom,micbias3-microvolt - - qcom,micbias4-microvolt - - "#sound-dai-cells" unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml new file mode 100644 index 000000000000..67ed7701b5d6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd939x-sdw.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd939x-sdw.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SoundWire devices on WCD9390/WCD9395 + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire devices. This bindings is for the devices. + +properties: + compatible: + const: sdw20217010e00 + + reg: + maxItems: 1 + + qcom,tx-port-mapping: + description: | + Specifies static port mapping between device and host tx ports. + In the order of the device port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + + qcom,rx-port-mapping: + description: | + Specifies static port mapping between device and host rx ports. + In the order of device port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 6 + maxItems: 6 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soundwire@3210000 { + #address-cells = <2>; + #size-cells = <0>; + reg = <0x03210000 0x2000>; + wcd938x_rx: codec@0,4 { + compatible = "sdw20217010e00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5 6>; + }; + }; + + soundwire@3230000 { + #address-cells = <2>; + #size-cells = <0>; + reg = <0x03230000 0x2000>; + wcd938x_tx: codec@0,3 { + compatible = "sdw20217010e00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 3 4 5>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml new file mode 100644 index 000000000000..6e76f6a8634f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd939x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCD9380/WCD9385 Audio Codec + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm WCD9390/WCD9395 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire devices. + The WCD9390/WCD9395 IC has a functionally separate USB-C Mux subsystem + accessible over an I2C interface. + The Audio Headphone and Microphone data path between the Codec and the USB-C Mux + subsystems are external to the IC, thus requiring DT port-endpoint graph description + to handle USB-C altmode & orientation switching for Audio Accessory Mode. + +allOf: + - $ref: dai-common.yaml# + - $ref: qcom,wcd93xx-common.yaml# + +properties: + compatible: + oneOf: + - const: qcom,wcd9390-codec + - items: + - const: qcom,wcd9395-codec + - const: qcom,wcd9390-codec + + mode-switch: + description: Flag the port as possible handler of altmode switching + type: boolean + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + A port node to link the WCD939x Codec node to USB MUX subsystems for the + purpose of handling altmode muxing and orientation switching to detect and + enable Audio Accessory Mode. + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + codec { + compatible = "qcom,wcd9390-codec"; + reset-gpios = <&tlmm 32 IRQ_TYPE_NONE>; + #sound-dai-cells = <1>; + qcom,tx-device = <&wcd939x_tx>; + qcom,rx-device = <&wcd939x_rx>; + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,hphl-jack-type-normally-closed; + qcom,ground-jack-type-normally-closed; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + }; + + /* ... */ + + soundwire@3210000 { + #address-cells = <2>; + #size-cells = <0>; + reg = <0x03210000 0x2000>; + wcd939x_rx: codec@0,4 { + compatible = "sdw20217010e00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5 6>; + }; + }; + + soundwire@3230000 { + #address-cells = <2>; + #size-cells = <0>; + reg = <0x03230000 0x2000>; + wcd938x_tx: codec@0,3 { + compatible = "sdw20217010e00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 3 4 5>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml new file mode 100644 index 000000000000..f78ba148ad25 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd93xx-common.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd93xx-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common properties for Qualcomm WCD93xx Audio Codec + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +properties: + reset-gpios: + description: GPIO spec for reset line to use + maxItems: 1 + + vdd-buck-supply: + description: A reference to the 1.8V buck supply + + vdd-rxtx-supply: + description: A reference to the 1.8V rx supply + + vdd-io-supply: + description: A reference to the 1.8V I/O supply + + vdd-mic-bias-supply: + description: A reference to the 3.8V mic bias supply + + qcom,tx-device: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: A reference to Soundwire tx device phandle + + qcom,rx-device: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: A reference to Soundwire rx device phandle + + qcom,micbias1-microvolt: + description: micbias1 voltage + minimum: 1800000 + maximum: 2850000 + + qcom,micbias2-microvolt: + description: micbias2 voltage + minimum: 1800000 + maximum: 2850000 + + qcom,micbias3-microvolt: + description: micbias3 voltage + minimum: 1800000 + maximum: 2850000 + + qcom,micbias4-microvolt: + description: micbias4 voltage + minimum: 1800000 + maximum: 2850000 + + qcom,hphl-jack-type-normally-closed: + description: Indicates that HPHL jack switch type is normally closed + type: boolean + + qcom,ground-jack-type-normally-closed: + description: Indicates that Headset Ground switch type is normally closed + type: boolean + + qcom,mbhc-headset-vthreshold-microvolt: + description: Voltage threshold value for headset detection + minimum: 0 + maximum: 2850000 + + qcom,mbhc-headphone-vthreshold-microvolt: + description: Voltage threshold value for headphone detection + minimum: 0 + maximum: 2850000 + + qcom,mbhc-buttons-vthreshold-microvolt: + description: + Array of 8 Voltage threshold values corresponding to headset + button0 - button7 + minItems: 8 + maxItems: 8 + + '#sound-dai-cells': + const: 1 + +required: + - reset-gpios + - qcom,tx-device + - qcom,rx-device + - qcom,micbias1-microvolt + - qcom,micbias2-microvolt + - qcom,micbias3-microvolt + - qcom,micbias4-microvolt + - "#sound-dai-cells" + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml index d717017b0fdb..22798d22d981 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml @@ -28,6 +28,10 @@ properties: description: Powerdown/Shutdown line to use (pin SD_N) maxItems: 1 + reset-gpios: + description: Powerdown/Shutdown line to use (pin SD_N) + maxItems: 1 + '#sound-dai-cells': const: 0 @@ -37,11 +41,16 @@ properties: required: - compatible - reg - - powerdown-gpios - '#sound-dai-cells' - vdd-1p8-supply - vdd-io-supply +oneOf: + - required: + - powerdown-gpios + - required: + - reset-gpios + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml new file mode 100644 index 000000000000..880196081a60 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt1015.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt1015.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT1015 Mono Class D Audio Amplifier + +maintainers: + - Jack Yu <jack.yu@realtek.com> + +properties: + compatible: + enum: + - realtek,rt1015 + + reg: + maxItems: 1 + + realtek,power-up-delay-ms: + description: Set a delay time for flush work to be completed, + this vlaue is adjustable depending on platform. + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@28 { + compatible = "realtek,rt1015"; + reg = <0x28>; + realtek,power-up-delay-ms = <50>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt1015.txt b/Documentation/devicetree/bindings/sound/rt1015.txt deleted file mode 100644 index e498966d436f..000000000000 --- a/Documentation/devicetree/bindings/sound/rt1015.txt +++ /dev/null @@ -1,23 +0,0 @@ -RT1015 Mono Class D Audio Amplifier - -This device supports I2C only. - -Required properties: - -- compatible : "realtek,rt1015". - -- reg : The I2C address of the device. - -Optional properties: - -- realtek,power-up-delay-ms - Set a delay time for flush work to be completed, - this value is adjustable depending on platform. - -Example: - -rt1015: codec@28 { - compatible = "realtek,rt1015"; - reg = <0x28>; - realtek,power-up-delay-ms = <50>; -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml index 760592599143..cbc7ba37362a 100644 --- a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml @@ -25,8 +25,11 @@ properties: description: Phandles to the codecs. $ref: /schemas/types.yaml#/definitions/phandle-array items: - - description: Phandle to the WM5110 audio codec. - - description: Phandle to the HDMI transmitter node. + - items: + - description: Phandle to the WM5110 audio codec. + - items: + - description: Phandle to the HDMI transmitter node. + samsung,audio-routing: description: | diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index cd421856409e..2d2998faff62 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -3864,14 +3864,16 @@ corresponding destructor. And next, set suspend/resume callbacks to the pci_driver:: - static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume); + static DEFINE_SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume); static struct pci_driver driver = { .name = KBUILD_MODNAME, .id_table = snd_my_ids, .probe = snd_my_probe, .remove = snd_my_remove, - .driver.pm = &snd_my_pm_ops, + .driver = { + .pm = &snd_my_pm_ops, + }, }; Module Parameters diff --git a/MAINTAINERS b/MAINTAINERS index 4bce9cfedfba..98226d076ef6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5042,6 +5042,7 @@ F: include/linux/mfd/cs42l43* F: include/sound/cs* F: sound/pci/hda/cirrus* F: sound/pci/hda/cs* +F: sound/pci/hda/hda_component* F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/soc/codecs/cs* @@ -20515,6 +20516,12 @@ F: include/uapi/sound/compress_* F: sound/core/compress_offload.c F: sound/soc/soc-compress.c +SOUND - CORE KUNIT TEST +M: Ivan Orlov <ivan.orlov0322@gmail.com> +L: linux-sound@vger.kernel.org +S: Supported +F: sound/core/sound_kunit.c + SOUND - DMAENGINE HELPERS M: Lars-Peter Clausen <lars@metafoo.de> S: Supported diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3b722e4c0f06..7c157bf92695 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1760,7 +1760,9 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, + {"CSC3554", }, {"CSC3556", }, + {"CSC3557", }, {"INT33FE", }, {"INT3515", }, /* Non-conforming _HID for Cirrus Logic already released */ diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 79d4254d1f9b..9f3d665cfdcf 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -522,7 +522,7 @@ void cs_dsp_cleanup_debugfs(struct cs_dsp *dsp) { cs_dsp_debugfs_clear(dsp); debugfs_remove_recursive(dsp->debugfs_root); - dsp->debugfs_root = NULL; + dsp->debugfs_root = ERR_PTR(-ENODEV); } EXPORT_SYMBOL_NS_GPL(cs_dsp_cleanup_debugfs, FW_CS_DSP); #else @@ -2246,6 +2246,11 @@ static int cs_dsp_common_init(struct cs_dsp *dsp) mutex_init(&dsp->pwr_lock); +#ifdef CONFIG_DEBUG_FS + /* Ensure this is invalid if client never provides a debugfs root */ + dsp->debugfs_root = ERR_PTR(-ENODEV); +#endif + return 0; } diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 8158e3cf5d6d..97b9c6392230 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -329,6 +329,19 @@ static const struct smi_node cs35l41_hda = { .bus_type = SMI_AUTO_DETECT, }; +static const struct smi_node cs35l54_hda = { + .instances = { + { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 }, + /* a 5th entry is an alias address, not a real device */ + { "cs35l54-hda_dummy_dev" }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + static const struct smi_node cs35l56_hda = { .instances = { { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 }, @@ -342,6 +355,19 @@ static const struct smi_node cs35l56_hda = { .bus_type = SMI_AUTO_DETECT, }; +static const struct smi_node cs35l57_hda = { + .instances = { + { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 }, + /* a 5th entry is an alias address, not a real device */ + { "cs35l57-hda_dummy_dev" }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + /* * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). @@ -350,7 +376,9 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, + { "CSC3554", (unsigned long)&cs35l54_hda }, { "CSC3556", (unsigned long)&cs35l56_hda }, + { "CSC3557", (unsigned long)&cs35l57_hda }, { "INT3515", (unsigned long)&int3515_data }, /* Non-conforming _HID for Cirrus Logic already released */ { "CLSA0100", (unsigned long)&cs35l41_hda }, diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index 657f5888a77b..e80a2c2cf3e7 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -20,7 +20,7 @@ soundwire-bus-y += irq.o endif #AMD driver -soundwire-amd-y := amd_manager.o +soundwire-amd-y := amd_init.o amd_manager.o obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o #Cadence Objs diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c new file mode 100644 index 000000000000..e45dc8261ab1 --- /dev/null +++ b/drivers/soundwire/amd_init.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * SoundWire AMD Manager Initialize routines + * + * Initializes and creates SDW devices based on ACPI and Hardware values + * + * Copyright 2024 Advanced Micro Devices, Inc. + */ + +#include <linux/acpi.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "amd_init.h" + +#define ACP_PAD_PULLDOWN_CTRL 0x0001448 +#define ACP_SW_PAD_KEEPER_EN 0x0001454 +#define AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9a +#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9f +#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7ffa +#define AMD_SDW0_PAD_EN_MASK 1 +#define AMD_SDW1_PAD_EN_MASK 0x10 +#define AMD_SDW_PAD_EN_MASK (AMD_SDW0_PAD_EN_MASK | AMD_SDW1_PAD_EN_MASK) + +static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) +{ + u32 val; + u32 pad_keeper_en_mask, pad_pulldown_ctrl_mask; + + switch (link_mask) { + case 1: + pad_keeper_en_mask = AMD_SDW0_PAD_EN_MASK; + pad_pulldown_ctrl_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK; + break; + case 2: + pad_keeper_en_mask = AMD_SDW1_PAD_EN_MASK; + pad_pulldown_ctrl_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK; + break; + case 3: + pad_keeper_en_mask = AMD_SDW_PAD_EN_MASK; + pad_pulldown_ctrl_mask = AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK; + break; + default: + dev_err(dev, "No SDW Links are enabled\n"); + return -ENODEV; + } + + val = readl(mmio + ACP_SW_PAD_KEEPER_EN); + val |= pad_keeper_en_mask; + writel(val, mmio + ACP_SW_PAD_KEEPER_EN); + val = readl(mmio + ACP_PAD_PULLDOWN_CTRL); + val &= pad_pulldown_ctrl_mask; + writel(val, mmio + ACP_PAD_PULLDOWN_CTRL); + return 0; +} + +static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx) +{ + int i; + + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + platform_device_unregister(ctx->pdev[i]); + } + + return 0; +} + +static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) +{ + struct sdw_amd_ctx *ctx; + struct acpi_device *adev; + struct resource *sdw_res; + struct acp_sdw_pdata sdw_pdata[2]; + struct platform_device_info pdevinfo[2]; + u32 link_mask; + int count, index; + int ret; + + if (!res) + return NULL; + + adev = acpi_fetch_acpi_dev(res->handle); + if (!adev) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; + dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); + ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent); + if (ret) + return NULL; + + /* + * we need to alloc/free memory manually and can't use devm: + * this routine may be called from a workqueue, and not from + * the parent .probe. + * If devm_ was used, the memory might never be freed on errors. + */ + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + ctx->count = count; + ctx->link_mask = res->link_mask; + sdw_res = kzalloc(sizeof(*sdw_res), GFP_KERNEL); + if (!sdw_res) { + kfree(ctx); + return NULL; + } + sdw_res->flags = IORESOURCE_MEM; + sdw_res->start = res->addr; + sdw_res->end = res->addr + res->reg_range; + memset(&pdevinfo, 0, sizeof(pdevinfo)); + link_mask = ctx->link_mask; + for (index = 0; index < count; index++) { + if (!(link_mask & BIT(index))) + continue; + + sdw_pdata[index].instance = index; + sdw_pdata[index].acp_sdw_lock = res->acp_lock; + pdevinfo[index].name = "amd_sdw_manager"; + pdevinfo[index].id = index; + pdevinfo[index].parent = res->parent; + pdevinfo[index].num_res = 1; + pdevinfo[index].res = sdw_res; + pdevinfo[index].data = &sdw_pdata[index]; + pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata); + pdevinfo[index].fwnode = acpi_fwnode_handle(adev); + ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]); + if (IS_ERR(ctx->pdev[index])) + goto err; + } + kfree(sdw_res); + return ctx; +err: + while (index--) { + if (!(link_mask & BIT(index))) + continue; + + platform_device_unregister(ctx->pdev[index]); + } + + kfree(sdw_res); + kfree(ctx); + return NULL; +} + +static int sdw_amd_startup(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + int i, ret; + + /* Startup SDW Manager devices */ + for (i = 0; i < ctx->count; i++) { + if (!(ctx->link_mask & BIT(i))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev); + ret = amd_sdw_manager_start(amd_manager); + if (ret) + return ret; + } + + return 0; +} + +int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx) +{ + *sdw_ctx = sdw_amd_probe_controller(res); + if (!*sdw_ctx) + return -ENODEV; + + return sdw_amd_startup(*sdw_ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT); + +void sdw_amd_exit(struct sdw_amd_ctx *ctx) +{ + sdw_amd_cleanup(ctx); + kfree(ctx->ids); + kfree(ctx); +} +EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT); + +int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) +{ + struct amd_sdw_manager *amd_manager; + struct sdw_bus *bus; + struct sdw_slave *slave; + struct list_head *node; + int index; + int i = 0; + int num_slaves = 0; + + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (!amd_manager) + return -ENODEV; + bus = &amd_manager->bus; + /* Calculate number of slaves */ + list_for_each(node, &bus->slaves) + num_slaves++; + } + + ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); + if (!ctx->ids) + return -ENOMEM; + ctx->num_slaves = num_slaves; + for (index = 0; index < ctx->count; index++) { + if (!(ctx->link_mask & BIT(index))) + continue; + amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); + if (amd_manager) { + bus = &amd_manager->bus; + list_for_each_entry(slave, &bus->slaves, node) { + ctx->ids[i].id = slave->id; + ctx->ids[i].link_id = bus->link_id; + i++; + } + } + } + return 0; +} +EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT); + +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD SoundWire Init Library"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/soundwire/amd_init.h b/drivers/soundwire/amd_init.h new file mode 100644 index 000000000000..928b0c707162 --- /dev/null +++ b/drivers/soundwire/amd_init.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. + */ + +#ifndef __AMD_INIT_H +#define __AMD_INIT_H + +#include <linux/soundwire/sdw_amd.h> + +int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager); + +#endif diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index f54bb4dd2d10..7cd24bd8e224 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -1,8 +1,8 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * SoundWire AMD Manager driver * - * Copyright 2023 Advanced Micro Devices, Inc. + * Copyright 2023-24 Advanced Micro Devices, Inc. */ #include <linux/completion.h> @@ -19,29 +19,13 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "bus.h" +#include "amd_init.h" #include "amd_manager.h" #define DRV_NAME "amd_sdw_manager" #define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus) -static void amd_enable_sdw_pads(struct amd_sdw_manager *amd_manager) -{ - u32 sw_pad_pulldown_val; - u32 val; - - mutex_lock(amd_manager->acp_sdw_lock); - val = readl(amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN); - val |= amd_manager->reg_mask->sw_pad_enable_mask; - writel(val, amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN); - usleep_range(1000, 1500); - - sw_pad_pulldown_val = readl(amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL); - sw_pad_pulldown_val &= amd_manager->reg_mask->sw_pad_pulldown_mask; - writel(sw_pad_pulldown_val, amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL); - mutex_unlock(amd_manager->acp_sdw_lock); -} - static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager) { u32 val; @@ -102,12 +86,11 @@ static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager) static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) { - struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask; u32 val; mutex_lock(amd_manager->acp_sdw_lock); val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); - val |= reg_mask->acp_sdw_intr_mask; + val |= sdw_manager_reg_mask_array[amd_manager->instance]; writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); mutex_unlock(amd_manager->acp_sdw_lock); @@ -120,12 +103,11 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager) static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager) { - struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask; u32 val; mutex_lock(amd_manager->acp_sdw_lock); val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); - val &= ~reg_mask->acp_sdw_intr_mask; + val &= ~sdw_manager_reg_mask_array[amd_manager->instance]; writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance)); mutex_unlock(amd_manager->acp_sdw_lock); @@ -864,23 +846,20 @@ static void amd_sdw_irq_thread(struct work_struct *work) writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7); } -static void amd_sdw_probe_work(struct work_struct *work) +int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager) { - struct amd_sdw_manager *amd_manager = container_of(work, struct amd_sdw_manager, - probe_work); struct sdw_master_prop *prop; int ret; prop = &amd_manager->bus.prop; if (!prop->hw_disabled) { - amd_enable_sdw_pads(amd_manager); ret = amd_init_sdw_manager(amd_manager); if (ret) - return; + return ret; amd_enable_sdw_interrupts(amd_manager); ret = amd_enable_sdw_manager(amd_manager); if (ret) - return; + return ret; amd_sdw_set_frameshape(amd_manager); } /* Enable runtime PM */ @@ -889,6 +868,7 @@ static void amd_sdw_probe_work(struct work_struct *work) pm_runtime_mark_last_busy(amd_manager->dev); pm_runtime_set_active(amd_manager->dev); pm_runtime_enable(amd_manager->dev); + return 0; } static int amd_sdw_manager_probe(struct platform_device *pdev) @@ -948,7 +928,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) return -EINVAL; } - amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance]; params = &amd_manager->bus.params; params->col = AMD_SDW_DEFAULT_COLUMNS; @@ -972,11 +951,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) dev_set_drvdata(dev, amd_manager); INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread); INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work); - INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work); - /* - * Instead of having lengthy probe sequence, use deferred probe. - */ - schedule_work(&amd_manager->probe_work); return 0; } @@ -986,7 +960,6 @@ static void amd_sdw_manager_remove(struct platform_device *pdev) int ret; pm_runtime_disable(&pdev->dev); - cancel_work_sync(&amd_manager->probe_work); amd_disable_sdw_interrupts(amd_manager); sdw_bus_master_delete(&amd_manager->bus); ret = amd_disable_sdw_manager(amd_manager); @@ -1215,5 +1188,5 @@ module_platform_driver(amd_sdw_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_DESCRIPTION("AMD SoundWire driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h index 5f040151a259..418b679e0b1a 100644 --- a/drivers/soundwire/amd_manager.h +++ b/drivers/soundwire/amd_manager.h @@ -1,6 +1,6 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ /* - * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (C) 2023-24 Advanced Micro Devices, Inc. All rights reserved. */ #ifndef __AMD_MANAGER_H @@ -243,16 +243,8 @@ static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] = { ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0} }; -static struct sdw_manager_reg_mask sdw_manager_reg_mask_array[2] = { - { - AMD_SDW0_PAD_KEEPER_EN_MASK, - AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK, - AMD_SDW0_EXT_INTR_MASK - }, - { - AMD_SDW1_PAD_KEEPER_EN_MASK, - AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK, +static u32 sdw_manager_reg_mask_array[AMD_SDW_MAX_MANAGER_COUNT] = { + AMD_SDW0_EXT_INTR_MASK, AMD_SDW1_EXT_INTR_MASK - } }; #endif diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 9ebdd0cd0b1c..91ab97a456fa 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -131,6 +131,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { .driver_data = (void *)intel_rooks_county, }, { + /* quirk used for NUC15 LAPRC710 skew */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), + }, + .driver_data = (void *)intel_rooks_county, + }, + { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 29cd11d5a3cf..23384a54d575 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -123,7 +123,6 @@ struct cs_dsp_client_ops; * @sysclk_mask: Mask of frequency bits within sysclk register (ADSP1 only) * @sysclk_shift: Shift of frequency bits within sysclk register (ADSP1 only) * @alg_regions: List of currently loaded algorithm regions - * @fw_file_name: Filename of the current firmware * @fw_name: Name of the current firmware * @fw_id: ID of the current firmware, obtained from the wmfw * @fw_id_version: Version of the firmware, obtained from the wmfw diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index ceecad74aef9..28a4eb77717f 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -1,11 +1,12 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ /* - * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (C) 2023-24 Advanced Micro Devices, Inc. All rights reserved. */ #ifndef __SDW_AMD_H #define __SDW_AMD_H +#include <linux/acpi.h> #include <linux/soundwire/sdw.h> /* AMD pm_runtime quirk definitions */ @@ -25,6 +26,7 @@ #define AMD_SDW_POWER_OFF_MODE 2 #define ACP_SDW0 0 #define ACP_SDW1 1 +#define AMD_SDW_MAX_MANAGER_COUNT 2 struct acp_sdw_pdata { u16 instance; @@ -32,12 +34,6 @@ struct acp_sdw_pdata { struct mutex *acp_sdw_lock; }; -struct sdw_manager_reg_mask { - u32 sw_pad_enable_mask; - u32 sw_pad_pulldown_mask; - u32 acp_sdw_intr_mask; -}; - /** * struct sdw_amd_dai_runtime: AMD sdw dai runtime data * @@ -59,10 +55,8 @@ struct sdw_amd_dai_runtime { * @dev: linux device * @mmio: SoundWire registers mmio base * @acp_mmio: acp registers mmio base - * @reg_mask: register mask structure per manager instance * @amd_sdw_irq_thread: SoundWire manager irq workqueue * @amd_sdw_work: peripheral status work queue - * @probe_work: SoundWire manager probe workqueue * @acp_sdw_lock: mutex to protect acp share register access * @status: peripheral devices status array * @num_din_ports: number of input ports @@ -83,10 +77,8 @@ struct amd_sdw_manager { void __iomem *mmio; void __iomem *acp_mmio; - struct sdw_manager_reg_mask *reg_mask; struct work_struct amd_sdw_irq_thread; struct work_struct amd_sdw_work; - struct work_struct probe_work; /* mutex to protect acp common register access */ struct mutex *acp_sdw_lock; @@ -106,4 +98,71 @@ struct amd_sdw_manager { struct sdw_amd_dai_runtime **dai_runtime_array; }; + +/** + * struct sdw_amd_acpi_info - Soundwire AMD information found in ACPI tables + * @handle: ACPI controller handle + * @count: maximum no of soundwire manager links supported on AMD platform. + * @link_mask: bit-wise mask listing links enabled by BIOS menu + */ +struct sdw_amd_acpi_info { + acpi_handle handle; + int count; + u32 link_mask; +}; + +/** + * struct sdw_amd_ctx - context allocated by the controller driver probe + * + * @count: link count + * @num_slaves: total number of devices exposed across all enabled links + * @link_mask: bit-wise mask listing SoundWire links reported by the + * Controller + * @ids: array of slave_id, representing Slaves exposed across all enabled + * links + * @pdev: platform device structure + */ +struct sdw_amd_ctx { + int count; + int num_slaves; + u32 link_mask; + struct sdw_extended_slave_id *ids; + struct platform_device *pdev[AMD_SDW_MAX_MANAGER_COUNT]; +}; + +/** + * struct sdw_amd_res - Soundwire AMD global resource structure, + * typically populated by the DSP driver/Legacy driver + * + * @addr: acp pci device resource start address + * @reg_range: ACP register range + * @link_mask: bit-wise mask listing links selected by the DSP driver/ + * legacy driver + * @count: link count + * @mmio_base: mmio base of SoundWire registers + * @handle: ACPI parent handle + * @parent: parent device + * @dev: device implementing hwparams and free callbacks + * @acp_lock: mutex protecting acp common registers access + */ +struct sdw_amd_res { + u32 addr; + u32 reg_range; + u32 link_mask; + int count; + void __iomem *mmio_base; + acpi_handle handle; + struct device *parent; + struct device *dev; + /* use to protect acp common registers access */ + struct mutex *acp_lock; +}; + +int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **ctx); + +void sdw_amd_exit(struct sdw_amd_ctx *ctx); + +int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx); + +int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info); #endif diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b05d5a87c313..c459809efee4 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -420,8 +420,6 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for exclusion of multiple callers * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use - * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert - * more than one chip select at once. * @setup: updates the device mode and clocking records used by a * device's SPI controller; protocol code may call this. This * must fail if an unrecognized or unsupported mode is requested. diff --git a/include/sound/ak4531_codec.h b/include/sound/ak4531_codec.h index 9a4429970d92..64402347d7a2 100644 --- a/include/sound/ak4531_codec.h +++ b/include/sound/ak4531_codec.h @@ -65,6 +65,9 @@ int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531, #ifdef CONFIG_PM void snd_ak4531_suspend(struct snd_ak4531 *ak4531); void snd_ak4531_resume(struct snd_ak4531 *ak4531); +#else +static inline void snd_ak4531_suspend(struct snd_ak4531 *ak4531) {} +static inline void snd_ak4531_resume(struct snd_ak4531 *ak4531) {} #endif #endif /* __SOUND_AK4531_CODEC_H */ diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h new file mode 100644 index 000000000000..f481148735e1 --- /dev/null +++ b/include/sound/cs-amp-lib.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2024 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS_AMP_LIB_H +#define CS_AMP_LIB_H + +#include <linux/efi.h> +#include <linux/types.h> + +struct cs_dsp; + +struct cirrus_amp_cal_data { + u32 calTarget[2]; + u32 calTime[2]; + s8 calAmbient; + u8 calStatus; + u16 calR; +} __packed; + +struct cirrus_amp_efi_data { + u32 size; + u32 count; + struct cirrus_amp_cal_data data[]; +} __packed; + +/** + * struct cirrus_amp_cal_controls - definition of firmware calibration controls + * @alg_id: ID of algorithm containing the controls. + * @mem_region: DSP memory region containing the controls. + * @ambient: Name of control for calAmbient value. + * @calr: Name of control for calR value. + * @status: Name of control for calStatus value. + * @checksum: Name of control for checksum value. + */ +struct cirrus_amp_cal_controls { + unsigned int alg_id; + int mem_region; + const char *ambient; + const char *calr; + const char *status; + const char *checksum; +}; + +int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data); +int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data); + +struct cs_amp_test_hooks { + efi_status_t (*get_efi_variable)(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf); + + int (*write_cal_coeff)(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val); +}; + +extern const struct cs_amp_test_hooks * const cs_amp_test_hooks; + +#endif /* CS_AMP_LIB_H */ diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index b24716ab2750..e0629699b563 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -12,6 +12,7 @@ #include <linux/firmware/cirrus/cs_dsp.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> +#include <sound/cs-amp-lib.h> #define CS35L56_DEVID 0x0000000 #define CS35L56_REVID 0x0000004 @@ -23,6 +24,9 @@ #define CS35L56_BLOCK_ENABLES2 0x000201C #define CS35L56_REFCLK_INPUT 0x0002C04 #define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C +#define CS35L56_OTP_MEM_53 0x00300D4 +#define CS35L56_OTP_MEM_54 0x00300D8 +#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_ASP1_ENABLES1 0x0004800 #define CS35L56_ASP1_CONTROL1 0x0004804 #define CS35L56_ASP1_CONTROL2 0x0004808 @@ -257,11 +261,15 @@ struct cs35l56_base { struct regmap *regmap; int irq; struct mutex irq_lock; + u8 type; u8 rev; bool init_done; bool fw_patched; bool secured; bool can_hibernate; + bool cal_data_valid; + s8 cal_index; + struct cirrus_amp_cal_data cal_data; struct gpio_desc *reset_gpio; }; @@ -269,6 +277,8 @@ extern struct regmap_config cs35l56_regmap_i2c; extern struct regmap_config cs35l56_regmap_spi; extern struct regmap_config cs35l56_regmap_sdw; +extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls; + extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; @@ -286,6 +296,7 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); +int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base); int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); diff --git a/include/sound/cs42l42.h b/include/sound/cs42l42.h index 3994e933db19..1bd8eee54f66 100644 --- a/include/sound/cs42l42.h +++ b/include/sound/cs42l42.h @@ -809,8 +809,7 @@ #define CS42L42_PLL_LOCK_TIMEOUT_US 1250 #define CS42L42_HP_ADC_EN_TIME_US 20000 #define CS42L42_PDN_DONE_POLL_US 1000 -#define CS42L42_PDN_DONE_TIMEOUT_US 200000 -#define CS42L42_PDN_DONE_TIME_MS 100 -#define CS42L42_FILT_DISCHARGE_TIME_MS 46 +#define CS42L42_PDN_DONE_TIMEOUT_US 235000 +#define CS42L42_PDN_DONE_TIME_MS 65 #endif /* __CS42L42_H */ diff --git a/include/sound/emux_synth.h b/include/sound/emux_synth.h index 1cc530434b97..3f7f365ed248 100644 --- a/include/sound/emux_synth.h +++ b/include/sound/emux_synth.h @@ -103,7 +103,7 @@ struct snd_emux { int ports[SNDRV_EMUX_MAX_PORTS]; /* The ports for this device */ struct snd_emux_port *portptrs[SNDRV_EMUX_MAX_PORTS]; int used; /* use counter */ - char *name; /* name of the device (internal) */ + const char *name; /* name of the device (internal) */ struct snd_rawmidi **vmidi; struct timer_list tlist; /* for pending note-offs */ int timer_active; diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 228114aca415..d849d9b24f13 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -181,4 +181,4 @@ hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enabl { return 0; } -#endif /* CONFIG_SND_SOC_SOF_HDA */ +#endif /* CONFIG_SND_SOC_SOF_HDA_MLINK */ diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index 55958711d697..5ff31e6d41c1 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -131,6 +131,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +#define AZX_REG_VS_LTRP_GB_MASK GENMASK(6, 0) + /* PCI space */ #define AZX_PCIREG_TCSEL 0x44 diff --git a/include/sound/pcm.h b/include/sound/pcm.h index cc175c623dac..210096f124ee 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -659,6 +659,18 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, flags = _snd_pcm_stream_lock_irqsave_nested(substream); \ } while (0) +/* definitions for guard(); use like guard(pcm_stream_lock) */ +DEFINE_LOCK_GUARD_1(pcm_stream_lock, struct snd_pcm_substream, + snd_pcm_stream_lock(_T->lock), + snd_pcm_stream_unlock(_T->lock)) +DEFINE_LOCK_GUARD_1(pcm_stream_lock_irq, struct snd_pcm_substream, + snd_pcm_stream_lock_irq(_T->lock), + snd_pcm_stream_unlock_irq(_T->lock)) +DEFINE_LOCK_GUARD_1(pcm_stream_lock_irqsave, struct snd_pcm_substream, + snd_pcm_stream_lock_irqsave(_T->lock, _T->flags), + snd_pcm_stream_unlock_irqrestore(_T->lock, _T->flags), + unsigned long flags) + /** * snd_pcm_group_for_each_entry - iterate over the linked substreams * @s: the iterator diff --git a/include/sound/sb.h b/include/sound/sb.h index f540339fb0c7..24970f36c38a 100644 --- a/include/sound/sb.h +++ b/include/sound/sb.h @@ -290,6 +290,9 @@ int snd_sbmixer_new(struct snd_sb *chip); #ifdef CONFIG_PM void snd_sbmixer_suspend(struct snd_sb *chip); void snd_sbmixer_resume(struct snd_sb *chip); +#else +static inline void snd_sbmixer_suspend(struct snd_sb *chip) {} +static inline void snd_sbmixer_resume(struct snd_sb *chip) {} #endif /* sb8_init.c */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 6defc5547ff9..39613b406b1d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1401,8 +1401,8 @@ void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, struct snd_soc_dai_link_component *cpus); struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, - struct of_phandle_args *args); -struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args); + const struct of_phandle_args *args); +struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args); struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, bool legacy_dai_naming); diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 9df7ac824efe..59cd014392c1 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -26,4 +26,11 @@ struct sof_ipc_dai_acpdmic_params { uint32_t pdm_ch; } __packed; +/* ACP_SDW Configuration Request - SOF_IPC_DAI_AMD_SDW_CONFIG */ +struct sof_ipc_dai_acp_sdw_params { + struct sof_ipc_hdr hdr; + u32 rate; + u32 channels; +} __packed; + #endif diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 4773a5f616a4..0764a80c17a9 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -89,6 +89,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_SP_VIRTUAL, /**< AMD ACP SP VIRTUAL */ SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ + SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ }; /* general purpose DAI configuration */ @@ -119,6 +120,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_acp_params acphs; struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; + struct sof_ipc_dai_acp_sdw_params acp_sdw; }; } __packed; diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 9aff384941de..99ca3e401fd1 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -103,7 +103,6 @@ struct tasdevice_priv { struct tm tm; enum device_catlog_id catlog_id; - const char *acpi_subsystem_id; unsigned char cal_binaryname[TASDEVICE_MAX_CHANNELS][64]; unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE]; unsigned char coef_binaryname[64]; diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 4d8ef71090af..4eed9028bb11 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -17,71 +17,76 @@ struct snd_soc_card; struct snd_soc_dapm_widget; struct snd_soc_dapm_path; -DECLARE_EVENT_CLASS(snd_soc_card, +DECLARE_EVENT_CLASS(snd_soc_dapm, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val), + TP_ARGS(dapm, val), TP_STRUCT__entry( - __string( name, card->name ) - __field( int, val ) + __string( card_name, dapm->card->name) + __string( comp_name, dapm->component ? dapm->component->name : "(none)") + __field( int, val) ), TP_fast_assign( - __assign_str(name, card->name); + __assign_str(card_name, dapm->card->name); + __assign_str(comp_name, dapm->component ? dapm->component->name : "(none)"); __entry->val = val; ), - TP_printk("card=%s val=%d", __get_str(name), (int)__entry->val) + TP_printk("card=%s component=%s val=%d", + __get_str(card_name), __get_str(comp_name), (int)__entry->val) ); -DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_start, +DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_start, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val) + TP_ARGS(dapm, val) ); -DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_done, +DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_done, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val) + TP_ARGS(dapm, val) ); DECLARE_EVENT_CLASS(snd_soc_dapm_basic, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card), + TP_ARGS(card, event), TP_STRUCT__entry( __string( name, card->name ) + __field( int, event ) ), TP_fast_assign( __assign_str(name, card->name); + __entry->event = event; ), - TP_printk("card=%s", __get_str(name)) + TP_printk("card=%s event=%d", __get_str(name), (int)__entry->event) ); DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_start, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card) + TP_ARGS(card, event) ); DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_done, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card) + TP_ARGS(card, event) ); diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h index dfe49547a7b0..5f4100c2cf04 100644 --- a/include/uapi/linux/virtio_snd.h +++ b/include/uapi/linux/virtio_snd.h @@ -8,6 +8,14 @@ #include <linux/virtio_types.h> /******************************************************************************* + * FEATURE BITS + */ +enum { + /* device supports control elements */ + VIRTIO_SND_F_CTLS = 0 +}; + +/******************************************************************************* * CONFIGURATION SPACE */ struct virtio_snd_config { @@ -17,6 +25,8 @@ struct virtio_snd_config { __le32 streams; /* # of available channel maps */ __le32 chmaps; + /* # of available control elements */ + __le32 controls; }; enum { @@ -55,6 +65,15 @@ enum { /* channel map control request types */ VIRTIO_SND_R_CHMAP_INFO = 0x0200, + /* control element request types */ + VIRTIO_SND_R_CTL_INFO = 0x0300, + VIRTIO_SND_R_CTL_ENUM_ITEMS, + VIRTIO_SND_R_CTL_READ, + VIRTIO_SND_R_CTL_WRITE, + VIRTIO_SND_R_CTL_TLV_READ, + VIRTIO_SND_R_CTL_TLV_WRITE, + VIRTIO_SND_R_CTL_TLV_COMMAND, + /* jack event types */ VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, VIRTIO_SND_EVT_JACK_DISCONNECTED, @@ -63,6 +82,9 @@ enum { VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, VIRTIO_SND_EVT_PCM_XRUN, + /* control element event types */ + VIRTIO_SND_EVT_CTL_NOTIFY = 0x1200, + /* common status codes */ VIRTIO_SND_S_OK = 0x8000, VIRTIO_SND_S_BAD_MSG, @@ -331,4 +353,136 @@ struct virtio_snd_chmap_info { __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; }; +/******************************************************************************* + * CONTROL ELEMENTS MESSAGES + */ +struct virtio_snd_ctl_hdr { + /* VIRTIO_SND_R_CTL_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + __le32 control_id; +}; + +/* supported roles for control elements */ +enum { + VIRTIO_SND_CTL_ROLE_UNDEFINED = 0, + VIRTIO_SND_CTL_ROLE_VOLUME, + VIRTIO_SND_CTL_ROLE_MUTE, + VIRTIO_SND_CTL_ROLE_GAIN +}; + +/* supported value types for control elements */ +enum { + VIRTIO_SND_CTL_TYPE_BOOLEAN = 0, + VIRTIO_SND_CTL_TYPE_INTEGER, + VIRTIO_SND_CTL_TYPE_INTEGER64, + VIRTIO_SND_CTL_TYPE_ENUMERATED, + VIRTIO_SND_CTL_TYPE_BYTES, + VIRTIO_SND_CTL_TYPE_IEC958 +}; + +/* supported access rights for control elements */ +enum { + VIRTIO_SND_CTL_ACCESS_READ = 0, + VIRTIO_SND_CTL_ACCESS_WRITE, + VIRTIO_SND_CTL_ACCESS_VOLATILE, + VIRTIO_SND_CTL_ACCESS_INACTIVE, + VIRTIO_SND_CTL_ACCESS_TLV_READ, + VIRTIO_SND_CTL_ACCESS_TLV_WRITE, + VIRTIO_SND_CTL_ACCESS_TLV_COMMAND +}; + +struct virtio_snd_ctl_info { + /* common header */ + struct virtio_snd_info hdr; + /* element role (VIRTIO_SND_CTL_ROLE_XXX) */ + __le32 role; + /* element value type (VIRTIO_SND_CTL_TYPE_XXX) */ + __le32 type; + /* element access right bit map (1 << VIRTIO_SND_CTL_ACCESS_XXX) */ + __le32 access; + /* # of members in the element value */ + __le32 count; + /* index for an element with a non-unique name */ + __le32 index; + /* name identifier string for the element */ + __u8 name[44]; + /* additional information about the element's value */ + union { + /* VIRTIO_SND_CTL_TYPE_INTEGER */ + struct { + /* minimum supported value */ + __le32 min; + /* maximum supported value */ + __le32 max; + /* fixed step size for value (0 = variable size) */ + __le32 step; + } integer; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 */ + struct { + /* minimum supported value */ + __le64 min; + /* maximum supported value */ + __le64 max; + /* fixed step size for value (0 = variable size) */ + __le64 step; + } integer64; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED */ + struct { + /* # of options supported for value */ + __le32 items; + } enumerated; + } value; +}; + +struct virtio_snd_ctl_enum_item { + /* option name */ + __u8 item[64]; +}; + +struct virtio_snd_ctl_iec958 { + /* AES/IEC958 channel status bits */ + __u8 status[24]; + /* AES/IEC958 subcode bits */ + __u8 subcode[147]; + /* nothing */ + __u8 pad; + /* AES/IEC958 subframe bits */ + __u8 dig_subframe[4]; +}; + +struct virtio_snd_ctl_value { + union { + /* VIRTIO_SND_CTL_TYPE_BOOLEAN|INTEGER value */ + __le32 integer[128]; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 value */ + __le64 integer64[64]; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED value (option indexes) */ + __le32 enumerated[128]; + /* VIRTIO_SND_CTL_TYPE_BYTES value */ + __u8 bytes[512]; + /* VIRTIO_SND_CTL_TYPE_IEC958 value */ + struct virtio_snd_ctl_iec958 iec958; + } value; +}; + +/* supported event reason types */ +enum { + /* element's value has changed */ + VIRTIO_SND_CTL_EVT_MASK_VALUE = 0, + /* element's information has changed */ + VIRTIO_SND_CTL_EVT_MASK_INFO, + /* element's metadata has changed */ + VIRTIO_SND_CTL_EVT_MASK_TLV +}; + +struct virtio_snd_ctl_event { + /* VIRTIO_SND_EVT_CTL_NOTIFY */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + __le16 control_id; + /* event reason bit map (1 << VIRTIO_SND_CTL_EVT_MASK_XXX) */ + __le16 mask; +}; + #endif /* VIRTIO_SND_IF_H */ diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h index 4ffe546aa409..4beca03405c0 100644 --- a/include/uapi/sound/intel/avs/tokens.h +++ b/include/uapi/sound/intel/avs/tokens.h @@ -19,6 +19,8 @@ enum avs_tplg_token { AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32 = 6, AVS_TKN_MANIFEST_NUM_PPLCFGS_U32 = 7, AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8, + AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 = 9, + AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32 = 10, /* struct avs_tplg_library */ AVS_TKN_LIBRARY_ID_U32 = 101, @@ -109,6 +111,8 @@ enum avs_tplg_token { AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705, AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706, AVS_TKN_MOD_KCONTROL_ID_U32 = 1707, + AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32 = 1708, + AVS_TKN_MOD_INIT_CONFIG_ID_U32 = 1709, /* struct avs_tplg_path_template */ AVS_TKN_PATH_TMPL_ID_U32 = 1801, @@ -125,6 +129,11 @@ enum avs_tplg_token { /* struct avs_tplg_kcontrol */ AVS_TKN_KCONTROL_ID_U32 = 2301, + + /* struct avs_tplg_init_config */ + AVS_TKN_INIT_CONFIG_ID_U32 = 2401, + AVS_TKN_INIT_CONFIG_PARAM_U8 = 2402, + AVS_TKN_INIT_CONFIG_LENGTH_U32 = 2403, }; #endif diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index ee5708934614..6bf00c09d30d 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -218,4 +218,8 @@ #define SOF_TKN_IMX_MICFIL_RATE 2000 #define SOF_TKN_IMX_MICFIL_CH 2001 +/* ACP SDW */ +#define SOF_TKN_AMD_ACP_SDW_RATE 2100 +#define SOF_TKN_AMD_ACP_SDW_CH 2101 + #endif diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index 0cd19a05db19..e68b4cb4df29 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -1126,7 +1126,6 @@ static void aoa_fabric_layout_remove(struct soundbus_dev *sdev) sdev->pcmname = NULL; } -#ifdef CONFIG_PM_SLEEP static int aoa_fabric_layout_suspend(struct device *dev) { struct layout_dev *ldev = dev_get_drvdata(dev); @@ -1147,11 +1146,9 @@ static int aoa_fabric_layout_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, aoa_fabric_layout_suspend, aoa_fabric_layout_resume); -#endif - static struct soundbus_driver aoa_soundbus_driver = { .name = "snd_aoa_soundbus_drv", .owner = THIS_MODULE, @@ -1159,9 +1156,7 @@ static struct soundbus_driver aoa_soundbus_driver = { .remove = aoa_fabric_layout_remove, .driver = { .owner = THIS_MODULE, -#ifdef CONFIG_PM_SLEEP .pm = &aoa_fabric_layout_pm_ops, -#endif } }; diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 8f24a3eea16b..2a295f610594 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -127,7 +127,7 @@ static void soundbus_device_shutdown(struct device *dev) /* soundbus_dev_attrs is declared in sysfs.c */ ATTRIBUTE_GROUPS(soundbus_dev); -static struct bus_type soundbus_bus_type = { +static const struct bus_type soundbus_bus_type = { .name = "aoa-soundbus", .probe = soundbus_probe, .uevent = soundbus_uevent, diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 0817ad21af74..c3340b8ff3da 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -737,10 +737,8 @@ static const struct snd_pcm_ops aaci_capture_ops = { /* * Power Management. */ -#ifdef CONFIG_PM static int aaci_do_suspend(struct snd_card *card) { - struct aaci *aaci = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); return 0; } @@ -763,12 +761,7 @@ static int aaci_resume(struct device *dev) return card ? aaci_do_resume(card) : 0; } -static SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); -#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops) -#else -#define AACI_DEV_PM_OPS NULL -#endif - +static DEFINE_SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); static const struct ac97_pcm ac97_defs[] = { [0] = { /* Front PCM */ @@ -1081,7 +1074,7 @@ MODULE_DEVICE_TABLE(amba, aaci_ids); static struct amba_driver aaci_driver = { .drv = { .name = DRIVER_NAME, - .pm = AACI_DEV_PM_OPS, + .pm = &aaci_dev_pm_ops, }, .probe = aaci_probe, .remove = aaci_remove, diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 2d83ad91f968..4c367e73b2c9 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -111,8 +111,6 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream) return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); } -#ifdef CONFIG_PM_SLEEP - static int pxa2xx_ac97_do_suspend(struct snd_card *card) { pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; @@ -164,8 +162,7 @@ static int pxa2xx_ac97_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume); static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = { .open = pxa2xx_ac97_pcm_open, @@ -277,9 +274,7 @@ static struct platform_driver pxa2xx_ac97_driver = { .remove_new = pxa2xx_ac97_remove, .driver = { .name = "pxa2xx-ac97", -#ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, -#endif }, }; diff --git a/sound/core/Kconfig b/sound/core/Kconfig index e41818e59a15..8077f481d84f 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -39,6 +39,23 @@ config SND_UMP_LEGACY_RAWMIDI legacy MIDI 1.0 byte streams is created for each UMP Endpoint. The device contains 16 substreams corresponding to UMP groups. +config SND_CORE_TEST + tristate "Sound core KUnit test" + depends on KUNIT + select SND_PCM + default KUNIT_ALL_TESTS + help + This options enables the sound core functions KUnit test. + + KUnit tests run during boot and output the results to the debug + log in TAP format (https://testanything.org/). Only useful for + kernel devs running KUnit test harness and are not for inclusion + into a production build. + + For more information on KUnit and unit tests in general, refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + config SND_COMPRESS_OFFLOAD tristate diff --git a/sound/core/Makefile b/sound/core/Makefile index f6526b337137..b8aa886198ab 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_UMP) += snd-ump.o +obj-$(CONFIG_SND_CORE_TEST) += sound_kunit.o + obj-$(CONFIG_SND_OSSEMUL) += oss/ obj-$(CONFIG_SND_SEQUENCER) += seq/ diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 619371aa9964..f0008fa2d839 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -127,9 +127,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) init_waitqueue_head(&runtime->sleep); data->stream.runtime = runtime; f->private_data = (void *)data; - mutex_lock(&compr->lock); - ret = compr->ops->open(&data->stream); - mutex_unlock(&compr->lock); + scoped_guard(mutex, &compr->lock) + ret = compr->ops->open(&data->stream); if (ret) { kfree(runtime); kfree(data); @@ -288,7 +287,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, return -EFAULT; stream = &data->stream; - mutex_lock(&stream->device->lock); + guard(mutex)(&stream->device->lock); /* write is allowed when stream is running or has been steup */ switch (stream->runtime->state) { case SNDRV_PCM_STATE_SETUP: @@ -296,7 +295,6 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, case SNDRV_PCM_STATE_RUNNING: break; default: - mutex_unlock(&stream->device->lock); return -EBADFD; } @@ -322,7 +320,6 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, pr_debug("stream prepared, Houston we are good to go\n"); } - mutex_unlock(&stream->device->lock); return retval; } @@ -339,7 +336,7 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, return -EFAULT; stream = &data->stream; - mutex_lock(&stream->device->lock); + guard(mutex)(&stream->device->lock); /* read is allowed when stream is running, paused, draining and setup * (yes setup is state which we transition to after stop, so if user @@ -350,11 +347,9 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: case SNDRV_PCM_STATE_DISCONNECTED: - retval = -EBADFD; - goto out; + return -EBADFD; case SNDRV_PCM_STATE_XRUN: - retval = -EPIPE; - goto out; + return -EPIPE; } avail = snd_compr_get_avail(stream); @@ -363,17 +358,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, if (avail > count) avail = count; - if (stream->ops->copy) { + if (stream->ops->copy) retval = stream->ops->copy(stream, buf, avail); - } else { - retval = -ENXIO; - goto out; - } + else + return -ENXIO; if (retval > 0) stream->runtime->total_bytes_transferred += retval; -out: - mutex_unlock(&stream->device->lock); return retval; } @@ -402,13 +393,12 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) stream = &data->stream; - mutex_lock(&stream->device->lock); + guard(mutex)(&stream->device->lock); switch (stream->runtime->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_XRUN: - retval = snd_compr_get_poll(stream) | EPOLLERR; - goto out; + return snd_compr_get_poll(stream) | EPOLLERR; default: break; } @@ -433,11 +423,9 @@ static __poll_t snd_compr_poll(struct file *f, poll_table *wait) retval = snd_compr_get_poll(stream); break; default: - retval = snd_compr_get_poll(stream) | EPOLLERR; - break; + return snd_compr_get_poll(stream) | EPOLLERR; } -out: - mutex_unlock(&stream->device->lock); + return retval; } @@ -465,7 +453,7 @@ static int snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) { int retval; - struct snd_compr_codec_caps *caps; + struct snd_compr_codec_caps *caps __free(kfree) = NULL; if (!stream->ops->get_codec_caps) return -ENXIO; @@ -476,12 +464,9 @@ snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) retval = stream->ops->get_codec_caps(stream, caps); if (retval) - goto out; + return retval; if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) - retval = -EFAULT; - -out: - kfree(caps); + return -EFAULT; return retval; } #endif /* !COMPR_CODEC_CAPS_OVERFLOW */ @@ -586,7 +571,7 @@ static int snd_compress_check_input(struct snd_compr_params *params) static int snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_compr_params *params; + struct snd_compr_params *params __free(kfree) = NULL; int retval; if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) { @@ -596,24 +581,22 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) */ params = memdup_user((void __user *)arg, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(params); + return PTR_ERR(no_free_ptr(params)); retval = snd_compress_check_input(params); if (retval) - goto out; + return retval; retval = snd_compr_allocate_buffer(stream, params); - if (retval) { - retval = -ENOMEM; - goto out; - } + if (retval) + return -ENOMEM; retval = stream->ops->set_params(stream, params); if (retval) - goto out; + return retval; if (stream->next_track) - goto out; + return retval; stream->metadata_set = false; stream->next_track = false; @@ -622,15 +605,13 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) } else { return -EPERM; } -out: - kfree(params); return retval; } static int snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) { - struct snd_codec *params; + struct snd_codec *params __free(kfree) = NULL; int retval; if (!stream->ops->get_params) @@ -641,12 +622,9 @@ snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) return -ENOMEM; retval = stream->ops->get_params(stream, params); if (retval) - goto out; + return retval; if (copy_to_user((char __user *)arg, params, sizeof(*params))) - retval = -EFAULT; - -out: - kfree(params); + return -EFAULT; return retval; } @@ -805,12 +783,10 @@ static void error_delayed_work(struct work_struct *work) stream = container_of(work, struct snd_compr_stream, error_work.work); - mutex_lock(&stream->device->lock); + guard(mutex)(&stream->device->lock); stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); wake_up(&stream->runtime->sleep); - - mutex_unlock(&stream->device->lock); } /** @@ -967,70 +943,52 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { struct snd_compr_file *data = f->private_data; struct snd_compr_stream *stream; - int retval = -ENOTTY; if (snd_BUG_ON(!data)) return -EFAULT; stream = &data->stream; - mutex_lock(&stream->device->lock); + guard(mutex)(&stream->device->lock); switch (_IOC_NR(cmd)) { case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): - retval = put_user(SNDRV_COMPRESS_VERSION, + return put_user(SNDRV_COMPRESS_VERSION, (int __user *)arg) ? -EFAULT : 0; - break; case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): - retval = snd_compr_get_caps(stream, arg); - break; + return snd_compr_get_caps(stream, arg); #ifndef COMPR_CODEC_CAPS_OVERFLOW case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): - retval = snd_compr_get_codec_caps(stream, arg); - break; + return snd_compr_get_codec_caps(stream, arg); #endif case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): - retval = snd_compr_set_params(stream, arg); - break; + return snd_compr_set_params(stream, arg); case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): - retval = snd_compr_get_params(stream, arg); - break; + return snd_compr_get_params(stream, arg); case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): - retval = snd_compr_set_metadata(stream, arg); - break; + return snd_compr_set_metadata(stream, arg); case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): - retval = snd_compr_get_metadata(stream, arg); - break; + return snd_compr_get_metadata(stream, arg); case _IOC_NR(SNDRV_COMPRESS_TSTAMP): - retval = snd_compr_tstamp(stream, arg); - break; + return snd_compr_tstamp(stream, arg); case _IOC_NR(SNDRV_COMPRESS_AVAIL): - retval = snd_compr_ioctl_avail(stream, arg); - break; + return snd_compr_ioctl_avail(stream, arg); case _IOC_NR(SNDRV_COMPRESS_PAUSE): - retval = snd_compr_pause(stream); - break; + return snd_compr_pause(stream); case _IOC_NR(SNDRV_COMPRESS_RESUME): - retval = snd_compr_resume(stream); - break; + return snd_compr_resume(stream); case _IOC_NR(SNDRV_COMPRESS_START): - retval = snd_compr_start(stream); - break; + return snd_compr_start(stream); case _IOC_NR(SNDRV_COMPRESS_STOP): - retval = snd_compr_stop(stream); - break; + return snd_compr_stop(stream); case _IOC_NR(SNDRV_COMPRESS_DRAIN): - retval = snd_compr_drain(stream); - break; + return snd_compr_drain(stream); case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): - retval = snd_compr_partial_drain(stream); - break; + return snd_compr_partial_drain(stream); case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): - retval = snd_compr_next_track(stream); - break; - + return snd_compr_next_track(stream); } - mutex_unlock(&stream->device->lock); - return retval; + + return -ENOTTY; } /* support of 32bit userspace on 64bit platforms */ diff --git a/sound/core/control.c b/sound/core/control.c index 59c8658966d4..8367fd485371 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -44,7 +44,6 @@ static int snd_ctl_remove_locked(struct snd_card *card, static int snd_ctl_open(struct inode *inode, struct file *file) { - unsigned long flags; struct snd_card *card; struct snd_ctl_file *ctl; int i, err; @@ -80,9 +79,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file) ctl->preferred_subdevice[i] = -1; ctl->pid = get_pid(task_pid(current)); file->private_data = ctl; - write_lock_irqsave(&card->ctl_files_rwlock, flags); - list_add_tail(&ctl->list, &card->ctl_files); - write_unlock_irqrestore(&card->ctl_files_rwlock, flags); + scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock) + list_add_tail(&ctl->list, &card->ctl_files); snd_card_unref(card); return 0; @@ -98,21 +96,18 @@ static int snd_ctl_open(struct inode *inode, struct file *file) static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl) { - unsigned long flags; struct snd_kctl_event *cread; - spin_lock_irqsave(&ctl->read_lock, flags); + guard(spinlock_irqsave)(&ctl->read_lock); while (!list_empty(&ctl->events)) { cread = snd_kctl_event(ctl->events.next); list_del(&cread->list); kfree(cread); } - spin_unlock_irqrestore(&ctl->read_lock, flags); } static int snd_ctl_release(struct inode *inode, struct file *file) { - unsigned long flags; struct snd_card *card; struct snd_ctl_file *ctl; struct snd_kcontrol *control; @@ -121,15 +116,17 @@ static int snd_ctl_release(struct inode *inode, struct file *file) ctl = file->private_data; file->private_data = NULL; card = ctl->card; - write_lock_irqsave(&card->ctl_files_rwlock, flags); - list_del(&ctl->list); - write_unlock_irqrestore(&card->ctl_files_rwlock, flags); - down_write(&card->controls_rwsem); - list_for_each_entry(control, &card->controls, list) - for (idx = 0; idx < control->count; idx++) - if (control->vd[idx].owner == ctl) - control->vd[idx].owner = NULL; - up_write(&card->controls_rwsem); + + scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock) + list_del(&ctl->list); + + scoped_guard(rwsem_write, &card->controls_rwsem) { + list_for_each_entry(control, &card->controls, list) + for (idx = 0; idx < control->count; idx++) + if (control->vd[idx].owner == ctl) + control->vd[idx].owner = NULL; + } + snd_fasync_free(ctl->fasync); snd_ctl_empty_read_queue(ctl); put_pid(ctl->pid); @@ -152,7 +149,6 @@ static int snd_ctl_release(struct inode *inode, struct file *file) void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id) { - unsigned long flags; struct snd_ctl_file *ctl; struct snd_kctl_event *ev; @@ -160,34 +156,34 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, return; if (card->shutdown) return; - read_lock_irqsave(&card->ctl_files_rwlock, flags); + + guard(read_lock_irqsave)(&card->ctl_files_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; #endif list_for_each_entry(ctl, &card->ctl_files, list) { if (!ctl->subscribed) continue; - spin_lock(&ctl->read_lock); - list_for_each_entry(ev, &ctl->events, list) { - if (ev->id.numid == id->numid) { - ev->mask |= mask; - goto _found; + scoped_guard(spinlock, &ctl->read_lock) { + list_for_each_entry(ev, &ctl->events, list) { + if (ev->id.numid == id->numid) { + ev->mask |= mask; + goto _found; + } } + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (ev) { + ev->id = *id; + ev->mask = mask; + list_add_tail(&ev->list, &ctl->events); + } else { + dev_err(card->dev, "No memory available to allocate event\n"); + } +_found: + wake_up(&ctl->change_sleep); } - ev = kzalloc(sizeof(*ev), GFP_ATOMIC); - if (ev) { - ev->id = *id; - ev->mask = mask; - list_add_tail(&ev->list, &ctl->events); - } else { - dev_err(card->dev, "No memory available to allocate event\n"); - } - _found: - wake_up(&ctl->change_sleep); - spin_unlock(&ctl->read_lock); snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN); } - read_unlock_irqrestore(&card->ctl_files_rwlock, flags); } EXPORT_SYMBOL(snd_ctl_notify); @@ -210,10 +206,9 @@ void snd_ctl_notify_one(struct snd_card *card, unsigned int mask, id.index += ioff; id.numid += ioff; snd_ctl_notify(card, mask, &id); - down_read(&snd_ctl_layer_rwsem); + guard(rwsem_read)(&snd_ctl_layer_rwsem); for (lops = snd_ctl_layer; lops; lops = lops->next) lops->lnotify(card, mask, kctl, ioff); - up_read(&snd_ctl_layer_rwsem); } EXPORT_SYMBOL(snd_ctl_notify_one); @@ -520,9 +515,9 @@ static int snd_ctl_add_replace(struct snd_card *card, if (snd_BUG_ON(!card || !kcontrol->info)) goto error; - down_write(&card->controls_rwsem); - err = __snd_ctl_add_replace(card, kcontrol, mode); - up_write(&card->controls_rwsem); + scoped_guard(rwsem_write, &card->controls_rwsem) + err = __snd_ctl_add_replace(card, kcontrol, mode); + if (err < 0) goto error; return 0; @@ -616,12 +611,8 @@ static inline int snd_ctl_remove_locked(struct snd_card *card, */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { - int ret; - - down_write(&card->controls_rwsem); - ret = snd_ctl_remove_locked(card, kcontrol); - up_write(&card->controls_rwsem); - return ret; + guard(rwsem_write)(&card->controls_rwsem); + return snd_ctl_remove_locked(card, kcontrol); } EXPORT_SYMBOL(snd_ctl_remove); @@ -638,17 +629,12 @@ EXPORT_SYMBOL(snd_ctl_remove); int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) { struct snd_kcontrol *kctl; - int ret; - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, id); - if (kctl == NULL) { - up_write(&card->controls_rwsem); + if (kctl == NULL) return -ENOENT; - } - ret = snd_ctl_remove_locked(card, kctl); - up_write(&card->controls_rwsem); - return ret; + return snd_ctl_remove_locked(card, kctl); } EXPORT_SYMBOL(snd_ctl_remove_id); @@ -667,27 +653,18 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, { struct snd_card *card = file->card; struct snd_kcontrol *kctl; - int idx, ret; + int idx; - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, id); - if (kctl == NULL) { - ret = -ENOENT; - goto error; - } - if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) { - ret = -EINVAL; - goto error; - } + if (kctl == NULL) + return -ENOENT; + if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) + return -EINVAL; for (idx = 0; idx < kctl->count; idx++) - if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) { - ret = -EBUSY; - goto error; - } - ret = snd_ctl_remove_locked(card, kctl); -error: - up_write(&card->controls_rwsem); - return ret; + if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) + return -EBUSY; + return snd_ctl_remove_locked(card, kctl); } /** @@ -764,18 +741,15 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_kcontrol *kctl; int saved_numid; - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, src_id); - if (kctl == NULL) { - up_write(&card->controls_rwsem); + if (kctl == NULL) return -ENOENT; - } saved_numid = kctl->id.numid; remove_hash_entries(card, kctl); kctl->id = *dst_id; kctl->id.numid = saved_numid; add_hash_entries(card, kctl); - up_write(&card->controls_rwsem); return 0; } EXPORT_SYMBOL(snd_ctl_rename_id); @@ -793,7 +767,7 @@ EXPORT_SYMBOL(snd_ctl_rename_id); void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, const char *name) { - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); remove_hash_entries(card, kctl); if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0) @@ -801,7 +775,6 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, name, kctl->id.name); add_hash_entries(card, kctl); - up_write(&card->controls_rwsem); } EXPORT_SYMBOL(snd_ctl_rename); @@ -859,12 +832,8 @@ EXPORT_SYMBOL(snd_ctl_find_numid_locked); struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) { - struct snd_kcontrol *kctl; - - down_read(&card->controls_rwsem); - kctl = snd_ctl_find_numid_locked(card, numid); - up_read(&card->controls_rwsem); - return kctl; + guard(rwsem_read)(&card->controls_rwsem); + return snd_ctl_find_numid_locked(card, numid); } EXPORT_SYMBOL(snd_ctl_find_numid); @@ -920,37 +889,30 @@ EXPORT_SYMBOL(snd_ctl_find_id_locked); struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, const struct snd_ctl_elem_id *id) { - struct snd_kcontrol *kctl; - - down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); - up_read(&card->controls_rwsem); - return kctl; + guard(rwsem_read)(&card->controls_rwsem); + return snd_ctl_find_id_locked(card, id); } EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, unsigned int cmd, void __user *arg) { - struct snd_ctl_card_info *info; + struct snd_ctl_card_info *info __free(kfree) = NULL; info = kzalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; - down_read(&snd_ioctl_rwsem); - info->card = card->number; - strscpy(info->id, card->id, sizeof(info->id)); - strscpy(info->driver, card->driver, sizeof(info->driver)); - strscpy(info->name, card->shortname, sizeof(info->name)); - strscpy(info->longname, card->longname, sizeof(info->longname)); - strscpy(info->mixername, card->mixername, sizeof(info->mixername)); - strscpy(info->components, card->components, sizeof(info->components)); - up_read(&snd_ioctl_rwsem); - if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) { - kfree(info); + scoped_guard(rwsem_read, &snd_ioctl_rwsem) { + info->card = card->number; + strscpy(info->id, card->id, sizeof(info->id)); + strscpy(info->driver, card->driver, sizeof(info->driver)); + strscpy(info->name, card->shortname, sizeof(info->name)); + strscpy(info->longname, card->longname, sizeof(info->longname)); + strscpy(info->mixername, card->mixername, sizeof(info->mixername)); + strscpy(info->components, card->components, sizeof(info->components)); + } + if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) return -EFAULT; - } - kfree(info); return 0; } @@ -960,37 +922,31 @@ static int snd_ctl_elem_list(struct snd_card *card, struct snd_kcontrol *kctl; struct snd_ctl_elem_id id; unsigned int offset, space, jidx; - int err = 0; offset = list->offset; space = list->space; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); list->count = card->controls_count; list->used = 0; - if (space > 0) { - list_for_each_entry(kctl, &card->controls, list) { - if (offset >= kctl->count) { - offset -= kctl->count; - continue; - } - for (jidx = offset; jidx < kctl->count; jidx++) { - snd_ctl_build_ioff(&id, kctl, jidx); - if (copy_to_user(list->pids + list->used, &id, - sizeof(id))) { - err = -EFAULT; - goto out; - } - list->used++; - if (!--space) - goto out; - } - offset = 0; + if (!space) + return 0; + list_for_each_entry(kctl, &card->controls, list) { + if (offset >= kctl->count) { + offset -= kctl->count; + continue; } + for (jidx = offset; jidx < kctl->count; jidx++) { + snd_ctl_build_ioff(&id, kctl, jidx); + if (copy_to_user(list->pids + list->used, &id, sizeof(id))) + return -EFAULT; + list->used++; + if (!--space) + return 0; + } + offset = 0; } - out: - up_read(&card->controls_rwsem); - return err; + return 0; } static int snd_ctl_elem_list_user(struct snd_card *card, @@ -1238,16 +1194,12 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, { struct snd_card *card = ctl->card; struct snd_kcontrol *kctl; - int result; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, &info->id); - if (kctl == NULL) - result = -ENOENT; - else - result = __snd_ctl_elem_info(card, kctl, info, ctl); - up_read(&card->controls_rwsem); - return result; + if (!kctl) + return -ENOENT; + return __snd_ctl_elem_info(card, kctl, info, ctl); } static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, @@ -1279,19 +1231,15 @@ static int snd_ctl_elem_read(struct snd_card *card, const u32 pattern = 0xdeadbeef; int ret; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, &control->id); - if (kctl == NULL) { - ret = -ENOENT; - goto unlock; - } + if (!kctl) + return -ENOENT; index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; - if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || kctl->get == NULL) { - ret = -EPERM; - goto unlock; - } + if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || !kctl->get) + return -EPERM; snd_ctl_build_ioff(&control->id, kctl, index_offset); @@ -1301,7 +1249,7 @@ static int snd_ctl_elem_read(struct snd_card *card, info.id = control->id; ret = __snd_ctl_elem_info(card, kctl, &info, NULL); if (ret < 0) - goto unlock; + return ret; #endif if (!snd_ctl_skip_validation(&info)) @@ -1311,7 +1259,7 @@ static int snd_ctl_elem_read(struct snd_card *card, ret = kctl->get(kctl, control); snd_power_unref(card); if (ret < 0) - goto unlock; + return ret; if (!snd_ctl_skip_validation(&info) && sanity_check_elem_value(card, control, &info, pattern) < 0) { dev_err(card->dev, @@ -1319,12 +1267,9 @@ static int snd_ctl_elem_read(struct snd_card *card, control->id.iface, control->id.device, control->id.subdevice, control->id.name, control->id.index); - ret = -EINVAL; - goto unlock; + return -EINVAL; } -unlock: - up_read(&card->controls_rwsem); - return ret; + return 0; } static int snd_ctl_elem_read_user(struct snd_card *card, @@ -1339,12 +1284,10 @@ static int snd_ctl_elem_read_user(struct snd_card *card, result = snd_ctl_elem_read(card, control); if (result < 0) - goto error; + return result; if (copy_to_user(_control, control, sizeof(*control))) - result = -EFAULT; - error: - kfree(control); + return -EFAULT; return result; } @@ -1406,23 +1349,21 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control) { - struct snd_ctl_elem_value *control; + struct snd_ctl_elem_value *control __free(kfree) = NULL; struct snd_card *card; int result; control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) - return PTR_ERR(control); + return PTR_ERR(no_free_ptr(control)); card = file->card; result = snd_ctl_elem_write(card, file, control); if (result < 0) - goto error; + return result; if (copy_to_user(_control, control, sizeof(*control))) - result = -EFAULT; - error: - kfree(control); + return -EFAULT; return result; } @@ -1433,25 +1374,18 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file, struct snd_ctl_elem_id id; struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; - int result; if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, &id); - if (kctl == NULL) { - result = -ENOENT; - } else { - vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; - if (vd->owner != NULL) - result = -EBUSY; - else { - vd->owner = file; - result = 0; - } - } - up_write(&card->controls_rwsem); - return result; + if (!kctl) + return -ENOENT; + vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; + if (vd->owner) + return -EBUSY; + vd->owner = file; + return 0; } static int snd_ctl_elem_unlock(struct snd_ctl_file *file, @@ -1461,27 +1395,20 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct snd_ctl_elem_id id; struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; - int result; if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; - down_write(&card->controls_rwsem); + guard(rwsem_write)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, &id); - if (kctl == NULL) { - result = -ENOENT; - } else { - vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; - if (vd->owner == NULL) - result = -EINVAL; - else if (vd->owner != file) - result = -EPERM; - else { - vd->owner = NULL; - result = 0; - } - } - up_write(&card->controls_rwsem); - return result; + if (!kctl) + return -ENOENT; + vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; + if (!vd->owner) + return -EINVAL; + if (vd->owner != file) + return -EPERM; + vd->owner = NULL; + return 0; } struct user_element { @@ -1763,11 +1690,9 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, private_size = value_sizes[info->type] * info->count; alloc_size = compute_user_elem_size(private_size, count); - down_write(&card->controls_rwsem); - if (check_user_elem_overflow(card, alloc_size)) { - err = -ENOMEM; - goto unlock; - } + guard(rwsem_write)(&card->controls_rwsem); + if (check_user_elem_overflow(card, alloc_size)) + return -ENOMEM; /* * Keep memory object for this userspace control. After passing this @@ -1777,13 +1702,12 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, */ err = snd_ctl_new(&kctl, count, access, file); if (err < 0) - goto unlock; + return err; memcpy(&kctl->id, &info->id, sizeof(kctl->id)); ue = kzalloc(alloc_size, GFP_KERNEL); if (!ue) { kfree(kctl); - err = -ENOMEM; - goto unlock; + return -ENOMEM; } kctl->private_data = ue; kctl->private_free = snd_ctl_elem_user_free; @@ -1801,7 +1725,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, err = snd_ctl_elem_init_enum_names(ue); if (err < 0) { snd_ctl_free_one(kctl); - goto unlock; + return err; } } @@ -1821,7 +1745,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE); if (err < 0) { snd_ctl_free_one(kctl); - goto unlock; + return err; } offset = snd_ctl_get_ioff(kctl, &info->id); snd_ctl_build_ioff(&info->id, kctl, offset); @@ -1832,9 +1756,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, * applications because the field originally means PID of a process * which locks the element. */ - unlock: - up_write(&card->controls_rwsem); - return err; + return 0; } static int snd_ctl_elem_add_user(struct snd_ctl_file *file, @@ -2036,34 +1958,29 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: return snd_ctl_subscribe_events(ctl, ip); case SNDRV_CTL_IOCTL_TLV_READ: - down_read(&ctl->card->controls_rwsem); - err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ); - up_read(&ctl->card->controls_rwsem); + scoped_guard(rwsem_read, &ctl->card->controls_rwsem) + err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ); return err; case SNDRV_CTL_IOCTL_TLV_WRITE: - down_write(&ctl->card->controls_rwsem); - err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE); - up_write(&ctl->card->controls_rwsem); + scoped_guard(rwsem_write, &ctl->card->controls_rwsem) + err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE); return err; case SNDRV_CTL_IOCTL_TLV_COMMAND: - down_write(&ctl->card->controls_rwsem); - err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD); - up_write(&ctl->card->controls_rwsem); + scoped_guard(rwsem_write, &ctl->card->controls_rwsem) + err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD); return err; case SNDRV_CTL_IOCTL_POWER: return -ENOPROTOOPT; case SNDRV_CTL_IOCTL_POWER_STATE: return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0; } - down_read(&snd_ioctl_rwsem); + + guard(rwsem_read)(&snd_ioctl_rwsem); list_for_each_entry(p, &snd_control_ioctls, list) { err = p->fioctl(card, ctl, cmd, arg); - if (err != -ENOIOCTLCMD) { - up_read(&snd_ioctl_rwsem); + if (err != -ENOIOCTLCMD) return err; - } } - up_read(&snd_ioctl_rwsem); dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } @@ -2155,9 +2072,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head * if (pn == NULL) return -ENOMEM; pn->fioctl = fcn; - down_write(&snd_ioctl_rwsem); + guard(rwsem_write)(&snd_ioctl_rwsem); list_add_tail(&pn->list, lists); - up_write(&snd_ioctl_rwsem); return 0; } @@ -2200,16 +2116,14 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, if (snd_BUG_ON(!fcn)) return -EINVAL; - down_write(&snd_ioctl_rwsem); + guard(rwsem_write)(&snd_ioctl_rwsem); list_for_each_entry(p, lists, list) { if (p->fioctl == fcn) { list_del(&p->list); - up_write(&snd_ioctl_rwsem); kfree(p); return 0; } } - up_write(&snd_ioctl_rwsem); snd_BUG(); return -EINVAL; } @@ -2256,9 +2170,8 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type) { struct snd_ctl_file *kctl; int subdevice = -1; - unsigned long flags; - read_lock_irqsave(&card->ctl_files_rwlock, flags); + guard(read_lock_irqsave)(&card->ctl_files_rwlock); list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == task_pid(current)) { subdevice = kctl->preferred_subdevice[type]; @@ -2266,7 +2179,6 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type) break; } } - read_unlock_irqrestore(&card->ctl_files_rwlock, flags); return subdevice; } EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice); @@ -2296,13 +2208,11 @@ int snd_ctl_request_layer(const char *module_name) if (module_name == NULL) return 0; - down_read(&snd_ctl_layer_rwsem); - for (lops = snd_ctl_layer; lops; lops = lops->next) - if (strcmp(lops->module_name, module_name) == 0) - break; - up_read(&snd_ctl_layer_rwsem); - if (lops) - return 0; + scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) { + for (lops = snd_ctl_layer; lops; lops = lops->next) + if (strcmp(lops->module_name, module_name) == 0) + return 0; + } return request_module(module_name); } EXPORT_SYMBOL_GPL(snd_ctl_request_layer); @@ -2319,16 +2229,15 @@ void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops) struct snd_card *card; int card_number; - down_write(&snd_ctl_layer_rwsem); - lops->next = snd_ctl_layer; - snd_ctl_layer = lops; - up_write(&snd_ctl_layer_rwsem); + scoped_guard(rwsem_write, &snd_ctl_layer_rwsem) { + lops->next = snd_ctl_layer; + snd_ctl_layer = lops; + } for (card_number = 0; card_number < SNDRV_CARDS; card_number++) { card = snd_card_ref(card_number); if (card) { - down_read(&card->controls_rwsem); - lops->lregister(card); - up_read(&card->controls_rwsem); + scoped_guard(rwsem_read, &card->controls_rwsem) + lops->lregister(card); snd_card_unref(card); } } @@ -2347,7 +2256,7 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops) { struct snd_ctl_layer_ops *lops2, *prev_lops2; - down_write(&snd_ctl_layer_rwsem); + guard(rwsem_write)(&snd_ctl_layer_rwsem); for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) { if (lops2 == lops) { if (!prev_lops2) @@ -2358,7 +2267,6 @@ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops) } prev_lops2 = lops2; } - up_write(&snd_ctl_layer_rwsem); } EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer); @@ -2379,25 +2287,29 @@ static const struct file_operations snd_ctl_f_ops = .fasync = snd_ctl_fasync, }; +/* call lops under rwsems; called from snd_ctl_dev_*() below() */ +#define call_snd_ctl_lops(_card, _op) \ + do { \ + struct snd_ctl_layer_ops *lops; \ + guard(rwsem_read)(&(_card)->controls_rwsem); \ + guard(rwsem_read)(&snd_ctl_layer_rwsem); \ + for (lops = snd_ctl_layer; lops; lops = lops->next) \ + lops->_op(_card); \ + } while (0) + /* * registration of the control device */ static int snd_ctl_dev_register(struct snd_device *device) { struct snd_card *card = device->device_data; - struct snd_ctl_layer_ops *lops; int err; err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, card->ctl_dev); if (err < 0) return err; - down_read(&card->controls_rwsem); - down_read(&snd_ctl_layer_rwsem); - for (lops = snd_ctl_layer; lops; lops = lops->next) - lops->lregister(card); - up_read(&snd_ctl_layer_rwsem); - up_read(&card->controls_rwsem); + call_snd_ctl_lops(card, lregister); return 0; } @@ -2408,23 +2320,15 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) { struct snd_card *card = device->device_data; struct snd_ctl_file *ctl; - struct snd_ctl_layer_ops *lops; - unsigned long flags; - read_lock_irqsave(&card->ctl_files_rwlock, flags); - list_for_each_entry(ctl, &card->ctl_files, list) { - wake_up(&ctl->change_sleep); - snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR); + scoped_guard(read_lock_irqsave, &card->ctl_files_rwlock) { + list_for_each_entry(ctl, &card->ctl_files, list) { + wake_up(&ctl->change_sleep); + snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR); + } } - read_unlock_irqrestore(&card->ctl_files_rwlock, flags); - - down_read(&card->controls_rwsem); - down_read(&snd_ctl_layer_rwsem); - for (lops = snd_ctl_layer; lops; lops = lops->next) - lops->ldisconnect(card); - up_read(&snd_ctl_layer_rwsem); - up_read(&card->controls_rwsem); + call_snd_ctl_lops(card, ldisconnect); return snd_unregister_device(card->ctl_dev); } @@ -2436,17 +2340,17 @@ static int snd_ctl_dev_free(struct snd_device *device) struct snd_card *card = device->device_data; struct snd_kcontrol *control; - down_write(&card->controls_rwsem); - while (!list_empty(&card->controls)) { - control = snd_kcontrol(card->controls.next); - __snd_ctl_remove(card, control, false); - } + scoped_guard(rwsem_write, &card->controls_rwsem) { + while (!list_empty(&card->controls)) { + control = snd_kcontrol(card->controls.next); + __snd_ctl_remove(card, control, false); + } #ifdef CONFIG_SND_CTL_FAST_LOOKUP - xa_destroy(&card->ctl_numids); - xa_destroy(&card->ctl_hash); + xa_destroy(&card->ctl_numids); + xa_destroy(&card->ctl_hash); #endif - up_write(&card->controls_rwsem); + } put_device(card->ctl_dev); return 0; } diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 63d787501066..934bb945e702 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -79,61 +79,56 @@ struct snd_ctl_elem_info32 { static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl, struct snd_ctl_elem_info32 __user *data32) { - struct snd_ctl_elem_info *data; + struct snd_ctl_elem_info *data __free(kfree) = NULL; int err; data = kzalloc(sizeof(*data), GFP_KERNEL); if (! data) return -ENOMEM; - err = -EFAULT; /* copy id */ if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) - goto error; + return -EFAULT; /* we need to copy the item index. * hope this doesn't break anything.. */ if (get_user(data->value.enumerated.item, &data32->value.enumerated.item)) - goto error; + return -EFAULT; err = snd_ctl_elem_info(ctl, data); if (err < 0) - goto error; + return err; /* restore info to 32bit */ - err = -EFAULT; /* id, type, access, count */ if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) || copy_to_user(&data32->type, &data->type, 3 * sizeof(u32))) - goto error; + return -EFAULT; if (put_user(data->owner, &data32->owner)) - goto error; + return -EFAULT; switch (data->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_INTEGER: if (put_user(data->value.integer.min, &data32->value.integer.min) || put_user(data->value.integer.max, &data32->value.integer.max) || put_user(data->value.integer.step, &data32->value.integer.step)) - goto error; + return -EFAULT; break; case SNDRV_CTL_ELEM_TYPE_INTEGER64: if (copy_to_user(&data32->value.integer64, &data->value.integer64, sizeof(data->value.integer64))) - goto error; + return -EFAULT; break; case SNDRV_CTL_ELEM_TYPE_ENUMERATED: if (copy_to_user(&data32->value.enumerated, &data->value.enumerated, sizeof(data->value.enumerated))) - goto error; + return -EFAULT; break; default: break; } - err = 0; - error: - kfree(data); - return err; + return 0; } /* read / write */ @@ -169,31 +164,25 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, int *countp) { struct snd_kcontrol *kctl; - struct snd_ctl_elem_info *info; + struct snd_ctl_elem_info *info __free(kfree) = NULL; int err; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_id_locked(card, id); - if (! kctl) { - up_read(&card->controls_rwsem); + if (!kctl) return -ENOENT; - } info = kzalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) { - up_read(&card->controls_rwsem); + if (info == NULL) return -ENOMEM; - } info->id = *id; err = snd_power_ref_and_wait(card); if (!err) err = kctl->info(kctl, info); snd_power_unref(card); - up_read(&card->controls_rwsem); if (err >= 0) { err = info->type; *countp = info->count; } - kfree(info); return err; } @@ -289,7 +278,7 @@ static int copy_ctl_value_to_user(void __user *userdata, static int ctl_elem_read_user(struct snd_card *card, void __user *userdata, void __user *valuep) { - struct snd_ctl_elem_value *data; + struct snd_ctl_elem_value *data __free(kfree) = NULL; int err, type, count; data = kzalloc(sizeof(*data), GFP_KERNEL); @@ -299,21 +288,18 @@ static int ctl_elem_read_user(struct snd_card *card, err = copy_ctl_value_from_user(card, data, userdata, valuep, &type, &count); if (err < 0) - goto error; + return err; err = snd_ctl_elem_read(card, data); if (err < 0) - goto error; - err = copy_ctl_value_to_user(userdata, valuep, data, type, count); - error: - kfree(data); - return err; + return err; + return copy_ctl_value_to_user(userdata, valuep, data, type, count); } static int ctl_elem_write_user(struct snd_ctl_file *file, void __user *userdata, void __user *valuep) { - struct snd_ctl_elem_value *data; + struct snd_ctl_elem_value *data __free(kfree) = NULL; struct snd_card *card = file->card; int err, type, count; @@ -324,15 +310,12 @@ static int ctl_elem_write_user(struct snd_ctl_file *file, err = copy_ctl_value_from_user(card, data, userdata, valuep, &type, &count); if (err < 0) - goto error; + return err; err = snd_ctl_elem_write(card, file, data); if (err < 0) - goto error; - err = copy_ctl_value_to_user(userdata, valuep, data, type, count); - error: - kfree(data); - return err; + return err; + return copy_ctl_value_to_user(userdata, valuep, data, type, count); } static int snd_ctl_elem_read_user_compat(struct snd_card *card, @@ -366,49 +349,44 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, struct snd_ctl_elem_info32 __user *data32, int replace) { - struct snd_ctl_elem_info *data; - int err; + struct snd_ctl_elem_info *data __free(kfree) = NULL; data = kzalloc(sizeof(*data), GFP_KERNEL); if (! data) return -ENOMEM; - err = -EFAULT; /* id, type, access, count */ \ if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) || copy_from_user(&data->type, &data32->type, 3 * sizeof(u32))) - goto error; + return -EFAULT; if (get_user(data->owner, &data32->owner)) - goto error; + return -EFAULT; switch (data->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_INTEGER: if (get_user(data->value.integer.min, &data32->value.integer.min) || get_user(data->value.integer.max, &data32->value.integer.max) || get_user(data->value.integer.step, &data32->value.integer.step)) - goto error; + return -EFAULT; break; case SNDRV_CTL_ELEM_TYPE_INTEGER64: if (copy_from_user(&data->value.integer64, &data32->value.integer64, sizeof(data->value.integer64))) - goto error; + return -EFAULT; break; case SNDRV_CTL_ELEM_TYPE_ENUMERATED: if (copy_from_user(&data->value.enumerated, &data32->value.enumerated, sizeof(data->value.enumerated))) - goto error; + return -EFAULT; data->value.enumerated.names_ptr = (uintptr_t)compat_ptr(data->value.enumerated.names_ptr); break; default: break; } - err = snd_ctl_elem_add(file, data, replace); - error: - kfree(data); - return err; + return snd_ctl_elem_add(file, data, replace); } enum { @@ -468,16 +446,13 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns #endif /* CONFIG_X86_X32_ABI */ } - down_read(&snd_ioctl_rwsem); + guard(rwsem_read)(&snd_ioctl_rwsem); list_for_each_entry(p, &snd_control_compat_ioctls, list) { if (p->fioctl) { err = p->fioctl(ctl->card, ctl, cmd, arg); - if (err != -ENOIOCTLCMD) { - up_read(&snd_ioctl_rwsem); + if (err != -ENOIOCTLCMD) return err; - } } } - up_read(&snd_ioctl_rwsem); return -ENOIOCTLCMD; } diff --git a/sound/core/control_led.c b/sound/core/control_led.c index a78eb48927c7..3d37e9fa7b9c 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -147,29 +147,27 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access, return; route = -1; found = false; - mutex_lock(&snd_ctl_led_mutex); - /* the card may not be registered (active) at this point */ - if (card && !snd_ctl_led_card_valid[card->number]) { - mutex_unlock(&snd_ctl_led_mutex); - return; - } - list_for_each_entry(lctl, &led->controls, list) { - if (lctl->kctl == kctl && lctl->index_offset == ioff) - found = true; - UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); - } - if (!found && kctl && card) { - lctl = kzalloc(sizeof(*lctl), GFP_KERNEL); - if (lctl) { - lctl->card = card; - lctl->access = access; - lctl->kctl = kctl; - lctl->index_offset = ioff; - list_add(&lctl->list, &led->controls); + scoped_guard(mutex, &snd_ctl_led_mutex) { + /* the card may not be registered (active) at this point */ + if (card && !snd_ctl_led_card_valid[card->number]) + return; + list_for_each_entry(lctl, &led->controls, list) { + if (lctl->kctl == kctl && lctl->index_offset == ioff) + found = true; UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); } + if (!found && kctl && card) { + lctl = kzalloc(sizeof(*lctl), GFP_KERNEL); + if (lctl) { + lctl->card = card; + lctl->access = access; + lctl->kctl = kctl; + lctl->index_offset = ioff; + list_add(&lctl->list, &led->controls); + UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); + } + } } - mutex_unlock(&snd_ctl_led_mutex); switch (led->mode) { case MODE_OFF: route = 1; break; case MODE_ON: route = 0; break; @@ -201,14 +199,13 @@ static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int i struct snd_ctl_led_ctl *lctl; unsigned int ret = 0; - mutex_lock(&snd_ctl_led_mutex); + guard(mutex)(&snd_ctl_led_mutex); lctl = snd_ctl_led_find(kctl, ioff); if (lctl && (access == 0 || access != lctl->access)) { ret = lctl->access; list_del(&lctl->list); kfree(lctl); } - mutex_unlock(&snd_ctl_led_mutex); return ret; } @@ -239,44 +236,36 @@ static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask, } } +DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) + static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, unsigned int group, bool set) { - struct snd_card *card; + struct snd_card *card __free(snd_card_unref) = NULL; struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsigned int ioff, access, new_access; - int err = 0; card = snd_card_ref(card_number); - if (card) { - down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id_locked(card, id); - if (kctl) { - ioff = snd_ctl_get_ioff(kctl, id); - vd = &kctl->vd[ioff]; - access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; - if (access != 0 && access != group_to_access(group)) { - err = -EXDEV; - goto unlock; - } - new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; - if (set) - new_access |= group_to_access(group); - if (new_access != vd->access) { - vd->access = new_access; - snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff); - } - } else { - err = -ENOENT; - } -unlock: - up_write(&card->controls_rwsem); - snd_card_unref(card); - } else { - err = -ENXIO; + if (!card) + return -ENXIO; + guard(rwsem_write)(&card->controls_rwsem); + kctl = snd_ctl_find_id_locked(card, id); + if (!kctl) + return -ENOENT; + ioff = snd_ctl_get_ioff(kctl, id); + vd = &kctl->vd[ioff]; + access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; + if (access != 0 && access != group_to_access(group)) + return -EXDEV; + new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; + if (set) + new_access |= group_to_access(group); + if (new_access != vd->access) { + vd->access = new_access; + snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff); } - return err; + return 0; } static void snd_ctl_led_refresh(void) @@ -312,7 +301,7 @@ repeat: static int snd_ctl_led_reset(int card_number, unsigned int group) { - struct snd_card *card; + struct snd_card *card __free(snd_card_unref) = NULL; struct snd_ctl_led *led; struct snd_ctl_led_ctl *lctl; struct snd_kcontrol_volatile *vd; @@ -322,26 +311,22 @@ static int snd_ctl_led_reset(int card_number, unsigned int group) if (!card) return -ENXIO; - mutex_lock(&snd_ctl_led_mutex); - if (!snd_ctl_led_card_valid[card_number]) { - mutex_unlock(&snd_ctl_led_mutex); - snd_card_unref(card); - return -ENXIO; - } - led = &snd_ctl_leds[group]; + scoped_guard(mutex, &snd_ctl_led_mutex) { + if (!snd_ctl_led_card_valid[card_number]) + return -ENXIO; + led = &snd_ctl_leds[group]; repeat: - list_for_each_entry(lctl, &led->controls, list) - if (lctl->card == card) { - vd = &lctl->kctl->vd[lctl->index_offset]; - vd->access &= ~group_to_access(group); - snd_ctl_led_ctl_destroy(lctl); - change = true; - goto repeat; - } - mutex_unlock(&snd_ctl_led_mutex); + list_for_each_entry(lctl, &led->controls, list) + if (lctl->card == card) { + vd = &lctl->kctl->vd[lctl->index_offset]; + vd->access &= ~group_to_access(group); + snd_ctl_led_ctl_destroy(lctl); + change = true; + goto repeat; + } + } if (change) snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0); - snd_card_unref(card); return 0; } @@ -353,9 +338,8 @@ static void snd_ctl_led_register(struct snd_card *card) if (snd_BUG_ON(card->number < 0 || card->number >= ARRAY_SIZE(snd_ctl_led_card_valid))) return; - mutex_lock(&snd_ctl_led_mutex); - snd_ctl_led_card_valid[card->number] = true; - mutex_unlock(&snd_ctl_led_mutex); + scoped_guard(mutex, &snd_ctl_led_mutex) + snd_ctl_led_card_valid[card->number] = true; /* the register callback is already called with held card->controls_rwsem */ list_for_each_entry(kctl, &card->controls, list) for (ioff = 0; ioff < kctl->count; ioff++) @@ -367,10 +351,10 @@ static void snd_ctl_led_register(struct snd_card *card) static void snd_ctl_led_disconnect(struct snd_card *card) { snd_ctl_led_sysfs_remove(card); - mutex_lock(&snd_ctl_led_mutex); - snd_ctl_led_card_valid[card->number] = false; - snd_ctl_led_clean(card); - mutex_unlock(&snd_ctl_led_mutex); + scoped_guard(mutex, &snd_ctl_led_mutex) { + snd_ctl_led_card_valid[card->number] = false; + snd_ctl_led_clean(card); + } snd_ctl_led_refresh(); } @@ -430,9 +414,8 @@ static ssize_t mode_store(struct device *dev, else return count; - mutex_lock(&snd_ctl_led_mutex); - led->mode = mode; - mutex_unlock(&snd_ctl_led_mutex); + scoped_guard(mutex, &snd_ctl_led_mutex) + led->mode = mode; snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0); return count; @@ -615,15 +598,15 @@ static ssize_t list_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev); - struct snd_card *card; + struct snd_card *card __free(snd_card_unref) = NULL; struct snd_ctl_led_ctl *lctl; size_t l = 0; card = snd_card_ref(led_card->number); if (!card) return -ENXIO; - down_read(&card->controls_rwsem); - mutex_lock(&snd_ctl_led_mutex); + guard(rwsem_read)(&card->controls_rwsem); + guard(mutex)(&snd_ctl_led_mutex); if (snd_ctl_led_card_valid[led_card->number]) { list_for_each_entry(lctl, &led_card->led->controls, list) { if (lctl->card != card) @@ -634,9 +617,6 @@ static ssize_t list_show(struct device *dev, lctl->kctl->id.numid + lctl->index_offset); } } - mutex_unlock(&snd_ctl_led_mutex); - up_read(&card->controls_rwsem); - snd_card_unref(card); return l; } diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index e97ff8cccb64..147c1fea4708 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -35,12 +35,12 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) unsigned long ticks; enum hrtimer_restart ret = HRTIMER_NORESTART; - spin_lock(&t->lock); - if (!t->running) - goto out; /* fast path */ - stime->in_callback = true; - ticks = t->sticks; - spin_unlock(&t->lock); + scoped_guard(spinlock, &t->lock) { + if (!t->running) + return HRTIMER_NORESTART; /* fast path */ + stime->in_callback = true; + ticks = t->sticks; + } /* calculate the drift */ delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt)); @@ -49,15 +49,13 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) snd_timer_interrupt(stime->timer, ticks); - spin_lock(&t->lock); + guard(spinlock)(&t->lock); if (t->running) { hrtimer_add_expires_ns(hrt, t->sticks * resolution); ret = HRTIMER_RESTART; } stime->in_callback = false; - out: - spin_unlock(&t->lock); return ret; } @@ -80,10 +78,10 @@ static int snd_hrtimer_close(struct snd_timer *t) struct snd_hrtimer *stime = t->private_data; if (stime) { - spin_lock_irq(&t->lock); - t->running = 0; /* just to be sure */ - stime->in_callback = 1; /* skip start/stop */ - spin_unlock_irq(&t->lock); + scoped_guard(spinlock_irq, &t->lock) { + t->running = 0; /* just to be sure */ + stime->in_callback = 1; /* skip start/stop */ + } hrtimer_cancel(&stime->hrt); kfree(stime); diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index de7476034f2c..09200df2932c 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -149,12 +149,12 @@ static int snd_hwdep_release(struct inode *inode, struct file * file) struct snd_hwdep *hw = file->private_data; struct module *mod = hw->card->module; - mutex_lock(&hw->open_mutex); - if (hw->ops.release) - err = hw->ops.release(hw, file); - if (hw->used > 0) - hw->used--; - mutex_unlock(&hw->open_mutex); + scoped_guard(mutex, &hw->open_mutex) { + if (hw->ops.release) + err = hw->ops.release(hw, file); + if (hw->used > 0) + hw->used--; + } wake_up(&hw->open_wait); snd_card_file_remove(hw->card, file); @@ -272,23 +272,23 @@ static int snd_hwdep_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)arg)) return -EFAULT; - mutex_lock(®ister_mutex); - - if (device < 0) - device = 0; - else if (device < SNDRV_MINOR_HWDEPS) - device++; - else - device = SNDRV_MINOR_HWDEPS; - - while (device < SNDRV_MINOR_HWDEPS) { - if (snd_hwdep_search(card, device)) - break; - device++; + + scoped_guard(mutex, ®ister_mutex) { + if (device < 0) + device = 0; + else if (device < SNDRV_MINOR_HWDEPS) + device++; + else + device = SNDRV_MINOR_HWDEPS; + + while (device < SNDRV_MINOR_HWDEPS) { + if (snd_hwdep_search(card, device)) + break; + device++; + } + if (device >= SNDRV_MINOR_HWDEPS) + device = -1; } - if (device >= SNDRV_MINOR_HWDEPS) - device = -1; - mutex_unlock(®ister_mutex); if (put_user(device, (int __user *)arg)) return -EFAULT; return 0; @@ -296,19 +296,18 @@ static int snd_hwdep_control_ioctl(struct snd_card *card, case SNDRV_CTL_IOCTL_HWDEP_INFO: { struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg; - int device, err; + int device; struct snd_hwdep *hwdep; if (get_user(device, &info->device)) return -EFAULT; - mutex_lock(®ister_mutex); - hwdep = snd_hwdep_search(card, device); - if (hwdep) - err = snd_hwdep_info(hwdep, info); - else - err = -ENXIO; - mutex_unlock(®ister_mutex); - return err; + scoped_guard(mutex, ®ister_mutex) { + hwdep = snd_hwdep_search(card, device); + if (!hwdep) + return -ENXIO; + return snd_hwdep_info(hwdep, info); + } + break; } } return -ENOIOCTLCMD; @@ -422,11 +421,9 @@ static int snd_hwdep_dev_register(struct snd_device *device) struct snd_card *card = hwdep->card; int err; - mutex_lock(®ister_mutex); - if (snd_hwdep_search(card, hwdep->device)) { - mutex_unlock(®ister_mutex); + guard(mutex)(®ister_mutex); + if (snd_hwdep_search(card, hwdep->device)) return -EBUSY; - } list_add_tail(&hwdep->list, &snd_hwdep_devices); err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device, @@ -434,7 +431,6 @@ static int snd_hwdep_dev_register(struct snd_device *device) if (err < 0) { dev_err(hwdep->dev, "unable to register\n"); list_del(&hwdep->list); - mutex_unlock(®ister_mutex); return err; } @@ -454,7 +450,6 @@ static int snd_hwdep_dev_register(struct snd_device *device) hwdep->ossreg = 1; } #endif - mutex_unlock(®ister_mutex); return 0; } @@ -464,12 +459,10 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) if (snd_BUG_ON(!hwdep)) return -ENXIO; - mutex_lock(®ister_mutex); - if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) { - mutex_unlock(®ister_mutex); + guard(mutex)(®ister_mutex); + if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) return -EINVAL; - } - mutex_lock(&hwdep->open_mutex); + guard(mutex)(&hwdep->open_mutex); wake_up(&hwdep->open_wait); #ifdef CONFIG_SND_OSSEMUL if (hwdep->ossreg) @@ -477,8 +470,6 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) #endif snd_unregister_device(hwdep->dev); list_del_init(&hwdep->list); - mutex_unlock(&hwdep->open_mutex); - mutex_unlock(®ister_mutex); return 0; } @@ -492,11 +483,10 @@ static void snd_hwdep_proc_read(struct snd_info_entry *entry, { struct snd_hwdep *hwdep; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_for_each_entry(hwdep, &snd_hwdep_devices, list) snd_iprintf(buffer, "%02i-%02i: %s\n", hwdep->card->number, hwdep->device, hwdep->name); - mutex_unlock(®ister_mutex); } static struct snd_info_entry *snd_hwdep_proc_entry; diff --git a/sound/core/info.c b/sound/core/info.c index e2f302e55bbb..1f5b8a3d9e3b 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -105,17 +105,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) { struct snd_info_private_data *data; struct snd_info_entry *entry; - loff_t ret = -EINVAL, size; + loff_t size; data = file->private_data; entry = data->entry; - mutex_lock(&entry->access); - if (entry->c.ops->llseek) { - ret = entry->c.ops->llseek(entry, - data->file_private_data, - file, offset, orig); - goto out; - } + guard(mutex)(&entry->access); + if (entry->c.ops->llseek) + return entry->c.ops->llseek(entry, + data->file_private_data, + file, offset, orig); size = entry->size; switch (orig) { @@ -126,21 +124,18 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) break; case SEEK_END: if (!size) - goto out; + return -EINVAL; offset += size; break; default: - goto out; + return -EINVAL; } if (offset < 0) - goto out; + return -EINVAL; if (size && offset > size) offset = size; file->f_pos = offset; - ret = offset; - out: - mutex_unlock(&entry->access); - return ret; + return offset; } static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, @@ -238,10 +233,10 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) struct snd_info_private_data *data; int mode, err; - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); err = alloc_info_private(entry, &data); if (err < 0) - goto unlock; + return err; mode = file->f_flags & O_ACCMODE; if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || @@ -257,14 +252,11 @@ static int snd_info_entry_open(struct inode *inode, struct file *file) } file->private_data = data; - mutex_unlock(&info_mutex); return 0; error: kfree(data); module_put(entry->module); - unlock: - mutex_unlock(&info_mutex); return err; } @@ -306,7 +298,6 @@ static ssize_t snd_info_text_entry_write(struct file *file, struct snd_info_buffer *buf; loff_t pos; size_t next; - int err = 0; if (!entry->c.text.write) return -EIO; @@ -317,34 +308,24 @@ static ssize_t snd_info_text_entry_write(struct file *file, /* don't handle too large text inputs */ if (next > 16 * 1024) return -EIO; - mutex_lock(&entry->access); + guard(mutex)(&entry->access); buf = data->wbuffer; if (!buf) { data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto error; - } + if (!buf) + return -ENOMEM; } if (next > buf->len) { char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); - if (!nbuf) { - err = -ENOMEM; - goto error; - } + if (!nbuf) + return -ENOMEM; kvfree(buf->buffer); buf->buffer = nbuf; buf->len = PAGE_ALIGN(next); } - if (copy_from_user(buf->buffer + pos, buffer, count)) { - err = -EFAULT; - goto error; - } + if (copy_from_user(buf->buffer + pos, buffer, count)) + return -EFAULT; buf->size = next; - error: - mutex_unlock(&entry->access); - if (err < 0) - return err; *offset = next; return count; } @@ -369,10 +350,10 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file) struct snd_info_private_data *data; int err; - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); err = alloc_info_private(entry, &data); if (err < 0) - goto unlock; + return err; data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); if (!data->rbuffer) { @@ -386,15 +367,12 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file) err = single_open(file, snd_info_seq_show, data); if (err < 0) goto error; - mutex_unlock(&info_mutex); return 0; error: kfree(data->rbuffer); kfree(data); module_put(entry->module); - unlock: - mutex_unlock(&info_mutex); return err; } @@ -549,7 +527,7 @@ int snd_info_card_register(struct snd_card *card) */ void snd_info_card_id_change(struct snd_card *card) { - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); if (card->proc_root_link) { proc_remove(card->proc_root_link); card->proc_root_link = NULL; @@ -558,7 +536,6 @@ void snd_info_card_id_change(struct snd_card *card) card->proc_root_link = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); - mutex_unlock(&info_mutex); } /* @@ -574,12 +551,11 @@ void snd_info_card_disconnect(struct snd_card *card) if (card->proc_root) proc_remove(card->proc_root->p); - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); if (card->proc_root) snd_info_clear_entries(card->proc_root); card->proc_root_link = NULL; card->proc_root = NULL; - mutex_unlock(&info_mutex); } /* @@ -703,9 +679,8 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent, entry->parent = parent; entry->module = module; if (parent) { - mutex_lock(&parent->access); + guard(mutex)(&parent->access); list_add_tail(&entry->list, &parent->children); - mutex_unlock(&parent->access); } return entry; } @@ -775,9 +750,8 @@ void snd_info_free_entry(struct snd_info_entry * entry) return; if (entry->p) { proc_remove(entry->p); - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); snd_info_clear_entries(entry); - mutex_unlock(&info_mutex); } /* free all children at first */ @@ -786,9 +760,8 @@ void snd_info_free_entry(struct snd_info_entry * entry) p = entry->parent; if (p) { - mutex_lock(&p->access); + guard(mutex)(&p->access); list_del(&entry->list); - mutex_unlock(&p->access); } kfree(entry->name); if (entry->private_free) @@ -804,15 +777,13 @@ static int __snd_info_register(struct snd_info_entry *entry) if (snd_BUG_ON(!entry)) return -ENXIO; root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; - mutex_lock(&info_mutex); + guard(mutex)(&info_mutex); if (entry->p || !root) - goto unlock; + return 0; if (S_ISDIR(entry->mode)) { p = proc_mkdir_mode(entry->name, entry->mode, root); - if (!p) { - mutex_unlock(&info_mutex); + if (!p) return -ENOMEM; - } } else { const struct proc_ops *ops; if (entry->content == SNDRV_INFO_CONTENT_DATA) @@ -821,15 +792,11 @@ static int __snd_info_register(struct snd_info_entry *entry) ops = &snd_info_text_entry_ops; p = proc_create_data(entry->name, entry->mode, root, ops, entry); - if (!p) { - mutex_unlock(&info_mutex); + if (!p) return -ENOMEM; - } proc_set_size(p, entry->size); } entry->p = p; - unlock: - mutex_unlock(&info_mutex); return 0; } diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index ebc714b2f46b..0dbbb8005570 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -29,20 +29,17 @@ int snd_oss_info_register(int dev, int num, char *string) return -ENXIO; if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS)) return -ENXIO; - mutex_lock(&strings); + guard(mutex)(&strings); if (string == NULL) { x = snd_sndstat_strings[num][dev]; kfree(x); x = NULL; } else { x = kstrdup(string, GFP_KERNEL); - if (x == NULL) { - mutex_unlock(&strings); + if (x == NULL) return -ENOMEM; - } } snd_sndstat_strings[num][dev] = x; - mutex_unlock(&strings); return 0; } EXPORT_SYMBOL(snd_oss_info_register); @@ -53,7 +50,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d char *str; snd_iprintf(buf, "\n%s:", id); - mutex_lock(&strings); + guard(mutex)(&strings); for (idx = 0; idx < SNDRV_CARDS; idx++) { str = snd_sndstat_strings[idx][dev]; if (str) { @@ -64,7 +61,6 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d snd_iprintf(buf, "%i: %s\n", idx, str); } } - mutex_unlock(&strings); if (ok < 0) snd_iprintf(buf, " NOT ENABLED IN CONFIG\n"); return ok; diff --git a/sound/core/init.c b/sound/core/init.c index 22c0d217b860..4ed5037d8693 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -284,30 +284,31 @@ static int snd_card_init(struct snd_card *card, struct device *parent, if (xid) strscpy(card->id, xid, sizeof(card->id)); err = 0; - mutex_lock(&snd_card_mutex); - if (idx < 0) /* first check the matching module-name slot */ - idx = get_slot_from_bitmask(idx, module_slot_match, module); - if (idx < 0) /* if not matched, assign an empty slot */ - idx = get_slot_from_bitmask(idx, check_empty_slot, module); - if (idx < 0) - err = -ENODEV; - else if (idx < snd_ecards_limit) { - if (test_bit(idx, snd_cards_lock)) - err = -EBUSY; /* invalid */ - } else if (idx >= SNDRV_CARDS) - err = -ENODEV; + scoped_guard(mutex, &snd_card_mutex) { + if (idx < 0) /* first check the matching module-name slot */ + idx = get_slot_from_bitmask(idx, module_slot_match, module); + if (idx < 0) /* if not matched, assign an empty slot */ + idx = get_slot_from_bitmask(idx, check_empty_slot, module); + if (idx < 0) + err = -ENODEV; + else if (idx < snd_ecards_limit) { + if (test_bit(idx, snd_cards_lock)) + err = -EBUSY; /* invalid */ + } else if (idx >= SNDRV_CARDS) + err = -ENODEV; + if (!err) { + set_bit(idx, snd_cards_lock); /* lock it */ + if (idx >= snd_ecards_limit) + snd_ecards_limit = idx + 1; /* increase the limit */ + } + } if (err < 0) { - mutex_unlock(&snd_card_mutex); dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", - idx, snd_ecards_limit - 1, err); + idx, snd_ecards_limit - 1, err); if (!card->managed) kfree(card); /* manually free here, as no destructor called */ return err; } - set_bit(idx, snd_cards_lock); /* lock it */ - if (idx >= snd_ecards_limit) - snd_ecards_limit = idx + 1; /* increase the limit */ - mutex_unlock(&snd_card_mutex); card->dev = parent; card->number = idx; #ifdef MODULE @@ -386,11 +387,10 @@ struct snd_card *snd_card_ref(int idx) { struct snd_card *card; - mutex_lock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); card = snd_cards[idx]; if (card) get_device(&card->card_dev); - mutex_unlock(&snd_card_mutex); return card; } EXPORT_SYMBOL_GPL(snd_card_ref); @@ -398,12 +398,8 @@ EXPORT_SYMBOL_GPL(snd_card_ref); /* return non-zero if a card is already locked */ int snd_card_locked(int card) { - int locked; - - mutex_lock(&snd_card_mutex); - locked = test_bit(card, snd_cards_lock); - mutex_unlock(&snd_card_mutex); - return locked; + guard(mutex)(&snd_card_mutex); + return test_bit(card, snd_cards_lock); } static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) @@ -427,15 +423,15 @@ static int snd_disconnect_release(struct inode *inode, struct file *file) { struct snd_monitor_file *df = NULL, *_df; - spin_lock(&shutdown_lock); - list_for_each_entry(_df, &shutdown_files, shutdown_list) { - if (_df->file == file) { - df = _df; - list_del_init(&df->shutdown_list); - break; + scoped_guard(spinlock, &shutdown_lock) { + list_for_each_entry(_df, &shutdown_files, shutdown_list) { + if (_df->file == file) { + df = _df; + list_del_init(&df->shutdown_list); + break; + } } } - spin_unlock(&shutdown_lock); if (likely(df)) { if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) @@ -501,27 +497,24 @@ void snd_card_disconnect(struct snd_card *card) if (!card) return; - spin_lock(&card->files_lock); - if (card->shutdown) { - spin_unlock(&card->files_lock); - return; - } - card->shutdown = 1; + scoped_guard(spinlock, &card->files_lock) { + if (card->shutdown) + return; + card->shutdown = 1; - /* replace file->f_op with special dummy operations */ - list_for_each_entry(mfile, &card->files_list, list) { - /* it's critical part, use endless loop */ - /* we have no room to fail */ - mfile->disconnected_f_op = mfile->file->f_op; + /* replace file->f_op with special dummy operations */ + list_for_each_entry(mfile, &card->files_list, list) { + /* it's critical part, use endless loop */ + /* we have no room to fail */ + mfile->disconnected_f_op = mfile->file->f_op; - spin_lock(&shutdown_lock); - list_add(&mfile->shutdown_list, &shutdown_files); - spin_unlock(&shutdown_lock); + scoped_guard(spinlock, &shutdown_lock) + list_add(&mfile->shutdown_list, &shutdown_files); - mfile->file->f_op = &snd_shutdown_f_ops; - fops_get(mfile->file->f_op); + mfile->file->f_op = &snd_shutdown_f_ops; + fops_get(mfile->file->f_op); + } } - spin_unlock(&card->files_lock); /* notify all connected devices about disconnection */ /* at this point, they cannot respond to any calls except release() */ @@ -544,10 +537,10 @@ void snd_card_disconnect(struct snd_card *card) } /* disable fops (user space) operations for ALSA API */ - mutex_lock(&snd_card_mutex); - snd_cards[card->number] = NULL; - clear_bit(card->number, snd_cards_lock); - mutex_unlock(&snd_card_mutex); + scoped_guard(mutex, &snd_card_mutex) { + snd_cards[card->number] = NULL; + clear_bit(card->number, snd_cards_lock); + } #ifdef CONFIG_PM wake_up(&card->power_sleep); @@ -569,11 +562,10 @@ void snd_card_disconnect_sync(struct snd_card *card) { snd_card_disconnect(card); - spin_lock_irq(&card->files_lock); + guard(spinlock_irq)(&card->files_lock); wait_event_lock_irq(card->remove_sleep, list_empty(&card->files_list), card->files_lock); - spin_unlock_irq(&card->files_lock); } EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); @@ -767,9 +759,8 @@ void snd_card_set_id(struct snd_card *card, const char *nid) /* check if user specified own card->id */ if (card->id[0] != '\0') return; - mutex_lock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); snd_card_set_id_no_lock(card, nid, nid); - mutex_unlock(&snd_card_mutex); } EXPORT_SYMBOL(snd_card_set_id); @@ -797,14 +788,11 @@ static ssize_t id_store(struct device *dev, struct device_attribute *attr, } memcpy(buf1, buf, copy); buf1[copy] = '\0'; - mutex_lock(&snd_card_mutex); - if (!card_id_ok(NULL, buf1)) { - mutex_unlock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); + if (!card_id_ok(NULL, buf1)) return -EEXIST; - } strcpy(card->id, buf1); snd_info_card_id_change(card); - mutex_unlock(&snd_card_mutex); return count; } @@ -897,26 +885,27 @@ int snd_card_register(struct snd_card *card) err = snd_device_register_all(card); if (err < 0) return err; - mutex_lock(&snd_card_mutex); - if (snd_cards[card->number]) { - /* already registered */ - mutex_unlock(&snd_card_mutex); - return snd_info_card_register(card); /* register pending info */ - } - if (*card->id) { - /* make a unique id name from the given string */ - char tmpid[sizeof(card->id)]; - memcpy(tmpid, card->id, sizeof(card->id)); - snd_card_set_id_no_lock(card, tmpid, tmpid); - } else { - /* create an id from either shortname or longname */ - const char *src; - src = *card->shortname ? card->shortname : card->longname; - snd_card_set_id_no_lock(card, src, - retrieve_id_from_card_name(src)); + scoped_guard(mutex, &snd_card_mutex) { + if (snd_cards[card->number]) { + /* already registered */ + return snd_info_card_register(card); /* register pending info */ + } + if (*card->id) { + /* make a unique id name from the given string */ + char tmpid[sizeof(card->id)]; + + memcpy(tmpid, card->id, sizeof(card->id)); + snd_card_set_id_no_lock(card, tmpid, tmpid); + } else { + /* create an id from either shortname or longname */ + const char *src; + + src = *card->shortname ? card->shortname : card->longname; + snd_card_set_id_no_lock(card, src, + retrieve_id_from_card_name(src)); + } + snd_cards[card->number] = card; } - snd_cards[card->number] = card; - mutex_unlock(&snd_card_mutex); err = snd_info_card_register(card); if (err < 0) return err; @@ -937,7 +926,7 @@ static void snd_card_info_read(struct snd_info_entry *entry, struct snd_card *card; for (idx = count = 0; idx < SNDRV_CARDS; idx++) { - mutex_lock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); card = snd_cards[idx]; if (card) { count++; @@ -949,7 +938,6 @@ static void snd_card_info_read(struct snd_info_entry *entry, snd_iprintf(buffer, " %s\n", card->longname); } - mutex_unlock(&snd_card_mutex); } if (!count) snd_iprintf(buffer, "--- no soundcards ---\n"); @@ -962,13 +950,12 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer) struct snd_card *card; for (idx = count = 0; idx < SNDRV_CARDS; idx++) { - mutex_lock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); card = snd_cards[idx]; if (card) { count++; snd_iprintf(buffer, "%s\n", card->longname); } - mutex_unlock(&snd_card_mutex); } if (!count) { snd_iprintf(buffer, "--- no soundcards ---\n"); @@ -985,12 +972,11 @@ static void snd_card_module_info_read(struct snd_info_entry *entry, struct snd_card *card; for (idx = 0; idx < SNDRV_CARDS; idx++) { - mutex_lock(&snd_card_mutex); + guard(mutex)(&snd_card_mutex); card = snd_cards[idx]; if (card) snd_iprintf(buffer, "%2i %s\n", idx, card->module->name); - mutex_unlock(&snd_card_mutex); } } #endif @@ -1072,15 +1058,13 @@ int snd_card_file_add(struct snd_card *card, struct file *file) mfile->file = file; mfile->disconnected_f_op = NULL; INIT_LIST_HEAD(&mfile->shutdown_list); - spin_lock(&card->files_lock); + guard(spinlock)(&card->files_lock); if (card->shutdown) { - spin_unlock(&card->files_lock); kfree(mfile); return -ENODEV; } list_add(&mfile->list, &card->files_list); get_device(&card->card_dev); - spin_unlock(&card->files_lock); return 0; } EXPORT_SYMBOL(snd_card_file_add); @@ -1102,22 +1086,21 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) { struct snd_monitor_file *mfile, *found = NULL; - spin_lock(&card->files_lock); - list_for_each_entry(mfile, &card->files_list, list) { - if (mfile->file == file) { - list_del(&mfile->list); - spin_lock(&shutdown_lock); - list_del(&mfile->shutdown_list); - spin_unlock(&shutdown_lock); - if (mfile->disconnected_f_op) - fops_put(mfile->disconnected_f_op); - found = mfile; - break; + scoped_guard(spinlock, &card->files_lock) { + list_for_each_entry(mfile, &card->files_list, list) { + if (mfile->file == file) { + list_del(&mfile->list); + scoped_guard(spinlock, &shutdown_lock) + list_del(&mfile->shutdown_list); + if (mfile->disconnected_f_op) + fops_put(mfile->disconnected_f_op); + found = mfile; + break; + } } + if (list_empty(&card->files_list)) + wake_up_all(&card->remove_sleep); } - if (list_empty(&card->files_list)) - wake_up_all(&card->remove_sleep); - spin_unlock(&card->files_lock); if (!found) { dev_err(card->dev, "card file remove problem (%p)\n", file); return -ENOENT; diff --git a/sound/core/jack.c b/sound/core/jack.c index e0f034e7275c..e08b2c4fbd1a 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -42,11 +42,9 @@ static int snd_jack_dev_disconnect(struct snd_device *device) #ifdef CONFIG_SND_JACK_INPUT_DEV struct snd_jack *jack = device->device_data; - mutex_lock(&jack->input_dev_lock); - if (!jack->input_dev) { - mutex_unlock(&jack->input_dev_lock); + guard(mutex)(&jack->input_dev_lock); + if (!jack->input_dev) return 0; - } /* If the input device is registered with the input subsystem * then we need to use a different deallocator. */ @@ -55,7 +53,6 @@ static int snd_jack_dev_disconnect(struct snd_device *device) else input_free_device(jack->input_dev); jack->input_dev = NULL; - mutex_unlock(&jack->input_dev_lock); #endif /* CONFIG_SND_JACK_INPUT_DEV */ return 0; } @@ -92,11 +89,9 @@ static int snd_jack_dev_register(struct snd_device *device) snprintf(jack->name, sizeof(jack->name), "%s %s", card->shortname, jack->id); - mutex_lock(&jack->input_dev_lock); - if (!jack->input_dev) { - mutex_unlock(&jack->input_dev_lock); + guard(mutex)(&jack->input_dev_lock); + if (!jack->input_dev) return 0; - } jack->input_dev->name = jack->name; @@ -121,7 +116,6 @@ static int snd_jack_dev_register(struct snd_device *device) if (err == 0) jack->registered = 1; - mutex_unlock(&jack->input_dev_lock); return err; } #endif /* CONFIG_SND_JACK_INPUT_DEV */ @@ -586,14 +580,9 @@ EXPORT_SYMBOL(snd_jack_new); void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) { WARN_ON(jack->registered); - mutex_lock(&jack->input_dev_lock); - if (!jack->input_dev) { - mutex_unlock(&jack->input_dev_lock); - return; - } - - jack->input_dev->dev.parent = parent; - mutex_unlock(&jack->input_dev_lock); + guard(mutex)(&jack->input_dev_lock); + if (jack->input_dev) + jack->input_dev->dev.parent = parent; } EXPORT_SYMBOL(snd_jack_set_parent); diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index dae2da380835..6a0508093ea6 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -130,13 +130,12 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer) if (mixer == NULL) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_volume || pslot->put_recsrc) result |= 1 << chn; } - mutex_unlock(&mixer->reg_mutex); return result; } @@ -148,13 +147,12 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer) if (mixer == NULL) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; if (pslot->put_volume && pslot->stereo) result |= 1 << chn; } - mutex_unlock(&mixer->reg_mutex); return result; } @@ -165,7 +163,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer) if (mixer == NULL) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ result = mixer->mask_recsrc; } else { @@ -177,7 +175,6 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer) result |= 1 << chn; } } - mutex_unlock(&mixer->reg_mutex); return result; } @@ -188,12 +185,12 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) if (mixer == NULL) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ unsigned int index; result = mixer->get_recsrc(fmixer, &index); if (result < 0) - goto unlock; + return result; result = 1 << index; } else { struct snd_mixer_oss_slot *pslot; @@ -209,8 +206,6 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) } } mixer->oss_recsrc = result; - unlock: - mutex_unlock(&mixer->reg_mutex); return result; } @@ -224,7 +219,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr if (mixer == NULL) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; @@ -250,7 +245,6 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr } } } - mutex_unlock(&mixer->reg_mutex); return result; } @@ -262,7 +256,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot) if (mixer == NULL || slot > 30) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); pslot = &mixer->slots[slot]; left = pslot->volume[0]; right = pslot->volume[1]; @@ -270,21 +264,15 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot) result = pslot->get_volume(fmixer, pslot, &left, &right); if (!pslot->stereo) right = left; - if (snd_BUG_ON(left < 0 || left > 100)) { - result = -EIO; - goto unlock; - } - if (snd_BUG_ON(right < 0 || right > 100)) { - result = -EIO; - goto unlock; - } + if (snd_BUG_ON(left < 0 || left > 100)) + return -EIO; + if (snd_BUG_ON(right < 0 || right > 100)) + return -EIO; if (result >= 0) { pslot->volume[0] = left; pslot->volume[1] = right; result = (left & 0xff) | ((right & 0xff) << 8); } - unlock: - mutex_unlock(&mixer->reg_mutex); return result; } @@ -297,7 +285,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer, if (mixer == NULL || slot > 30) return -EIO; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); pslot = &mixer->slots[slot]; if (left > 100) left = 100; @@ -308,12 +296,10 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer, if (pslot->put_volume) result = pslot->put_volume(fmixer, pslot, left, right); if (result < 0) - goto unlock; + return result; pslot->volume[0] = left; pslot->volume[1] = right; result = (left & 0xff) | ((right & 0xff) << 8); - unlock: - mutex_unlock(&mixer->reg_mutex); return result; } @@ -532,37 +518,31 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, unsigned int numid, int *left, int *right) { - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; if (numid == ID_UNKNOWN) return; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_numid_locked(card, numid); - if (!kctl) { - up_read(&card->controls_rwsem); + if (!kctl) return; - } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) - goto __unalloc; + return; if (kctl->info(kctl, uinfo)) - goto __unalloc; + return; if (kctl->get(kctl, uctl)) - goto __unalloc; + return; if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN && uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1) - goto __unalloc; + return; *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]); if (uinfo->count > 1) *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]); - __unalloc: - up_read(&card->controls_rwsem); - kfree(uctl); - kfree(uinfo); } static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, @@ -571,27 +551,25 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, int *left, int *right, int route) { - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; if (numid == ID_UNKNOWN) return; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_numid_locked(card, numid); - if (!kctl) { - up_read(&card->controls_rwsem); + if (!kctl) return; - } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) - goto __unalloc; + return; if (kctl->info(kctl, uinfo)) - goto __unalloc; + return; if (kctl->get(kctl, uctl)) - goto __unalloc; + return; if (!uctl->value.integer.value[0]) { *left = 0; if (uinfo->count == 1) @@ -599,10 +577,6 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, } if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1]) *right = 0; - __unalloc: - up_read(&card->controls_rwsem); - kfree(uctl); - kfree(uinfo); } static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer, @@ -636,41 +610,35 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, unsigned int numid, int left, int right) { - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; int res; if (numid == ID_UNKNOWN) return; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_numid_locked(card, numid); - if (!kctl) { - up_read(&card->controls_rwsem); + if (!kctl) return; - } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) - goto __unalloc; + return; if (kctl->info(kctl, uinfo)) - goto __unalloc; + return; if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN && uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1) - goto __unalloc; + return; uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max); if (uinfo->count > 1) uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max); res = kctl->put(kctl, uctl); if (res < 0) - goto __unalloc; + return; if (res > 0) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - __unalloc: - up_read(&card->controls_rwsem); - kfree(uctl); - kfree(uinfo); } static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, @@ -679,26 +647,24 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, int left, int right, int route) { - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; struct snd_kcontrol *kctl; struct snd_card *card = fmixer->card; int res; if (numid == ID_UNKNOWN) return; - down_read(&card->controls_rwsem); + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_ctl_find_numid_locked(card, numid); - if (!kctl) { - up_read(&card->controls_rwsem); + if (!kctl) return; - } uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) - goto __unalloc; + return; if (kctl->info(kctl, uinfo)) - goto __unalloc; + return; if (uinfo->count > 1) { uctl->value.integer.value[0] = left > 0 ? 1 : 0; uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; @@ -711,13 +677,9 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, } res = kctl->put(kctl, uctl); if (res < 0) - goto __unalloc; + return; if (res > 0) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - __unalloc: - up_read(&card->controls_rwsem); - kfree(uctl); - kfree(uinfo); } static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer, @@ -822,28 +784,24 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *pslot; struct slot *slot; - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err, idx; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (uinfo == NULL || uctl == NULL) { - err = -ENOMEM; - goto __free_only; - } - down_read(&card->controls_rwsem); + if (uinfo == NULL || uctl == NULL) + return -ENOMEM; + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); - if (! kctl) { - err = -ENOENT; - goto __unlock; - } + if (!kctl) + return -ENOENT; err = kctl->info(kctl, uinfo); if (err < 0) - goto __unlock; + return err; err = kctl->get(kctl, uctl); if (err < 0) - goto __unlock; + return err; for (idx = 0; idx < 32; idx++) { if (!(mixer->mask_recsrc & (1 << idx))) continue; @@ -858,13 +816,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned break; } } - err = 0; - __unlock: - up_read(&card->controls_rwsem); - __free_only: - kfree(uctl); - kfree(uinfo); - return err; + return 0; } static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index) @@ -874,26 +826,22 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *pslot; struct slot *slot = NULL; - struct snd_ctl_elem_info *uinfo; - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err; unsigned int idx; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (uinfo == NULL || uctl == NULL) { - err = -ENOMEM; - goto __free_only; - } - down_read(&card->controls_rwsem); + if (uinfo == NULL || uctl == NULL) + return -ENOMEM; + guard(rwsem_read)(&card->controls_rwsem); kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); - if (! kctl) { - err = -ENOENT; - goto __unlock; - } + if (!kctl) + return -ENOENT; err = kctl->info(kctl, uinfo); if (err < 0) - goto __unlock; + return err; for (idx = 0; idx < 32; idx++) { if (!(mixer->mask_recsrc & (1 << idx))) continue; @@ -907,20 +855,14 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned break; slot = NULL; } - if (! slot) - goto __unlock; + if (!slot) + return 0; for (idx = 0; idx < uinfo->count; idx++) uctl->value.enumerated.item[idx] = slot->capture_item; err = kctl->put(kctl, uctl); if (err > 0) snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - err = 0; - __unlock: - up_read(&card->controls_rwsem); - __free_only: - kfree(uctl); - kfree(uinfo); - return err; + return 0; } struct snd_mixer_oss_assign_table { @@ -931,34 +873,26 @@ struct snd_mixer_oss_assign_table { static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item) { - struct snd_ctl_elem_info *info; + struct snd_ctl_elem_info *info __free(kfree) = NULL; struct snd_kcontrol *kcontrol; struct snd_card *card = mixer->card; int err; - down_read(&card->controls_rwsem); - kcontrol = snd_mixer_oss_test_id(mixer, name, index); - if (kcontrol == NULL) { - up_read(&card->controls_rwsem); - return 0; - } - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - up_read(&card->controls_rwsem); - return -ENOMEM; - } - err = kcontrol->info(kcontrol, info); - if (err < 0) { - up_read(&card->controls_rwsem); - kfree(info); - return err; + scoped_guard(rwsem_read, &card->controls_rwsem) { + kcontrol = snd_mixer_oss_test_id(mixer, name, index); + if (kcontrol == NULL) + return 0; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + err = kcontrol->info(kcontrol, info); + if (err < 0) + return err; + slot->numid[item] = kcontrol->id.numid; } - slot->numid[item] = kcontrol->id.numid; - up_read(&card->controls_rwsem); if (info->count > slot->channels) slot->channels = info->count; slot->present |= 1 << item; - kfree(info); return 0; } @@ -1068,24 +1002,19 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */ if (snd_mixer_oss_build_test_all(mixer, ptr, &slot)) return 0; - down_read(&mixer->card->controls_rwsem); + guard(rwsem_read)(&mixer->card->controls_rwsem); kctl = NULL; if (!ptr->index) kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); if (kctl) { - struct snd_ctl_elem_info *uinfo; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); - if (! uinfo) { - up_read(&mixer->card->controls_rwsem); + if (!uinfo) return -ENOMEM; - } - if (kctl->info(kctl, uinfo)) { - up_read(&mixer->card->controls_rwsem); - kfree(uinfo); + if (kctl->info(kctl, uinfo)) return 0; - } strcpy(str, ptr->name); if (!strcmp(str, "Master")) strcpy(str, "Mix"); @@ -1097,20 +1026,15 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, } else { for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) { uinfo->value.enumerated.item = slot.capture_item; - if (kctl->info(kctl, uinfo)) { - up_read(&mixer->card->controls_rwsem); - kfree(uinfo); + if (kctl->info(kctl, uinfo)) return 0; - } if (!strcmp(uinfo->value.enumerated.name, str)) { slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; break; } } } - kfree(uinfo); } - up_read(&mixer->card->controls_rwsem); if (slot.present != 0) { pslot = kmalloc(sizeof(slot), GFP_KERNEL); if (! pslot) @@ -1183,7 +1107,7 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry, struct snd_mixer_oss *mixer = entry->private_data; int i; - mutex_lock(&mixer->reg_mutex); + guard(mutex)(&mixer->reg_mutex); for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) { struct slot *p; @@ -1198,7 +1122,6 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry, else snd_iprintf(buffer, "\"\" 0\n"); } - mutex_unlock(&mixer->reg_mutex); } static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, @@ -1225,9 +1148,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, cptr = snd_info_get_str(str, cptr, sizeof(str)); if (! *str) { /* remove the entry */ - mutex_lock(&mixer->reg_mutex); - mixer_slot_clear(&mixer->slots[ch]); - mutex_unlock(&mixer->reg_mutex); + scoped_guard(mutex, &mixer->reg_mutex) + mixer_slot_clear(&mixer->slots[ch]); continue; } snd_info_get_str(idxstr, cptr, sizeof(idxstr)); @@ -1236,28 +1158,27 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, pr_err("ALSA: mixer_oss: invalid index %d\n", idx); continue; } - mutex_lock(&mixer->reg_mutex); - slot = (struct slot *)mixer->slots[ch].private_data; - if (slot && slot->assigned && - slot->assigned->index == idx && ! strcmp(slot->assigned->name, str)) - /* not changed */ - goto __unlock; - tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); - if (!tbl) - goto __unlock; - tbl->oss_id = ch; - tbl->name = kstrdup(str, GFP_KERNEL); - if (! tbl->name) { - kfree(tbl); - goto __unlock; - } - tbl->index = idx; - if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) { - kfree(tbl->name); - kfree(tbl); + scoped_guard(mutex, &mixer->reg_mutex) { + slot = (struct slot *)mixer->slots[ch].private_data; + if (slot && slot->assigned && + slot->assigned->index == idx && !strcmp(slot->assigned->name, str)) + /* not changed */ + break; + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (!tbl) + break; + tbl->oss_id = ch; + tbl->name = kstrdup(str, GFP_KERNEL); + if (!tbl->name) { + kfree(tbl); + break; + } + tbl->index = idx; + if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) { + kfree(tbl->name); + kfree(tbl); + } } - __unlock: - mutex_unlock(&mixer->reg_mutex); } } diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 728c211142d1..7386982cf40e 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -377,7 +377,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, snd_pcm_hw_param_t var, unsigned int best, int *dir) { - struct snd_pcm_hw_params *save = NULL; + struct snd_pcm_hw_params *save __free(kfree) = NULL; int v; unsigned int saved_min; int last = 0; @@ -404,38 +404,30 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, saved_min = min; min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); if (min >= 0) { - struct snd_pcm_hw_params *params1; + struct snd_pcm_hw_params *params1 __free(kfree) = NULL; if (max < 0) goto _end; if ((unsigned int)min == saved_min && mindir == valdir) goto _end; params1 = kmalloc(sizeof(*params1), GFP_KERNEL); - if (params1 == NULL) { - kfree(save); + if (params1 == NULL) return -ENOMEM; - } *params1 = *save; max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); - if (max < 0) { - kfree(params1); + if (max < 0) goto _end; - } if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { *params = *params1; last = 1; } - kfree(params1); } else { *params = *save; max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); - if (max < 0) { - kfree(save); + if (max < 0) return max; - } last = 1; } _end: - kfree(save); if (last) v = snd_pcm_hw_param_last(pcm, params, var, dir); else @@ -789,7 +781,7 @@ static int choose_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, unsigned int best_rate) { const struct snd_interval *it; - struct snd_pcm_hw_params *save; + struct snd_pcm_hw_params *save __free(kfree) = NULL; unsigned int rate, prev; save = kmalloc(sizeof(*save), GFP_KERNEL); @@ -808,10 +800,8 @@ static int choose_rate(struct snd_pcm_substream *substream, ret = snd_pcm_hw_param_set(substream, params, SNDRV_PCM_HW_PARAM_RATE, rate, 0); - if (ret == (int)rate) { - kfree(save); + if (ret == (int)rate) return rate; - } *params = *save; } prev = rate; @@ -821,7 +811,6 @@ static int choose_rate(struct snd_pcm_substream *substream, } /* not found, use the nearest rate */ - kfree(save); return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); } @@ -1634,9 +1623,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) break; result = 0; set_current_state(TASK_INTERRUPTIBLE); - snd_pcm_stream_lock_irq(substream); - state = runtime->state; - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) + state = runtime->state; if (state != SNDRV_PCM_STATE_RUNNING) { set_current_state(TASK_RUNNING); break; @@ -1847,7 +1835,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) struct snd_pcm_substream *substream; int err; int direct; - struct snd_pcm_hw_params *params; + struct snd_pcm_hw_params *params __free(kfree) = NULL; unsigned int formats = 0; const struct snd_mask *format_mask; int fmt; @@ -1873,7 +1861,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params); if (err < 0) - goto error; + return err; format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); for (fmt = 0; fmt < 32; ++fmt) { if (snd_mask_test(format_mask, fmt)) { @@ -1883,9 +1871,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) } } - error: - kfree(params); - return err < 0 ? err : formats; + return formats; } static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format) @@ -2347,7 +2333,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, { struct snd_pcm_oss_setup *setup; - mutex_lock(&pcm->streams[stream].oss.setup_mutex); + guard(mutex)(&pcm->streams[stream].oss.setup_mutex); do { for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) { @@ -2358,7 +2344,6 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, out: if (setup) *rsetup = *setup; - mutex_unlock(&pcm->streams[stream].oss.setup_mutex); } static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream) @@ -2853,23 +2838,23 @@ static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait) if (psubstream != NULL) { struct snd_pcm_runtime *runtime = psubstream->runtime; poll_wait(file, &runtime->sleep, wait); - snd_pcm_stream_lock_irq(psubstream); - if (runtime->state != SNDRV_PCM_STATE_DRAINING && - (runtime->state != SNDRV_PCM_STATE_RUNNING || - snd_pcm_oss_playback_ready(psubstream))) - mask |= EPOLLOUT | EPOLLWRNORM; - snd_pcm_stream_unlock_irq(psubstream); + scoped_guard(pcm_stream_lock_irq, psubstream) { + if (runtime->state != SNDRV_PCM_STATE_DRAINING && + (runtime->state != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_playback_ready(psubstream))) + mask |= EPOLLOUT | EPOLLWRNORM; + } } if (csubstream != NULL) { struct snd_pcm_runtime *runtime = csubstream->runtime; snd_pcm_state_t ostate; poll_wait(file, &runtime->sleep, wait); - snd_pcm_stream_lock_irq(csubstream); - ostate = runtime->state; - if (ostate != SNDRV_PCM_STATE_RUNNING || - snd_pcm_oss_capture_ready(csubstream)) - mask |= EPOLLIN | EPOLLRDNORM; - snd_pcm_stream_unlock_irq(csubstream); + scoped_guard(pcm_stream_lock_irq, csubstream) { + ostate = runtime->state; + if (ostate != SNDRV_PCM_STATE_RUNNING || + snd_pcm_oss_capture_ready(csubstream)) + mask |= EPOLLIN | EPOLLRDNORM; + } if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) { struct snd_pcm_oss_file ofile; memset(&ofile, 0, sizeof(ofile)); @@ -2964,7 +2949,7 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry, { struct snd_pcm_str *pstr = entry->private_data; struct snd_pcm_oss_setup *setup = pstr->oss.setup_list; - mutex_lock(&pstr->oss.setup_mutex); + guard(mutex)(&pstr->oss.setup_mutex); while (setup) { snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n", setup->task_name, @@ -2978,7 +2963,6 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry, setup->nosilence ? " no-silence" : ""); setup = setup->next; } - mutex_unlock(&pstr->oss.setup_mutex); } static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr) @@ -3004,12 +2988,11 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, struct snd_pcm_oss_setup *setup, *setup1, template; while (!snd_info_get_line(buffer, line, sizeof(line))) { - mutex_lock(&pstr->oss.setup_mutex); + guard(mutex)(&pstr->oss.setup_mutex); memset(&template, 0, sizeof(template)); ptr = snd_info_get_str(task_name, line, sizeof(task_name)); if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) { snd_pcm_oss_proc_free_setup_list(pstr); - mutex_unlock(&pstr->oss.setup_mutex); continue; } for (setup = pstr->oss.setup_list; setup; setup = setup->next) { @@ -3049,7 +3032,6 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, setup = kmalloc(sizeof(*setup), GFP_KERNEL); if (! setup) { buffer->error = -ENOMEM; - mutex_unlock(&pstr->oss.setup_mutex); return; } if (pstr->oss.setup_list == NULL) @@ -3063,12 +3045,10 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, if (! template.task_name) { kfree(setup); buffer->error = -ENOMEM; - mutex_unlock(&pstr->oss.setup_mutex); return; } } *setup = template; - mutex_unlock(&pstr->oss.setup_mutex); } } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index d0788126cbab..dc37f3508dc7 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -91,9 +91,8 @@ static int snd_pcm_control_ioctl(struct snd_card *card, if (get_user(device, (int __user *)arg)) return -EFAULT; - mutex_lock(®ister_mutex); - device = snd_pcm_next(card, device); - mutex_unlock(®ister_mutex); + scoped_guard(mutex, ®ister_mutex) + device = snd_pcm_next(card, device); if (put_user(device, (int __user *)arg)) return -EFAULT; return 0; @@ -106,7 +105,6 @@ static int snd_pcm_control_ioctl(struct snd_card *card, struct snd_pcm *pcm; struct snd_pcm_str *pstr; struct snd_pcm_substream *substream; - int err; info = (struct snd_pcm_info __user *)arg; if (get_user(device, &info->device)) @@ -118,35 +116,23 @@ static int snd_pcm_control_ioctl(struct snd_card *card, stream = array_index_nospec(stream, 2); if (get_user(subdevice, &info->subdevice)) return -EFAULT; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); pcm = snd_pcm_get(card, device); - if (pcm == NULL) { - err = -ENXIO; - goto _error; - } + if (pcm == NULL) + return -ENXIO; pstr = &pcm->streams[stream]; - if (pstr->substream_count == 0) { - err = -ENOENT; - goto _error; - } - if (subdevice >= pstr->substream_count) { - err = -ENXIO; - goto _error; - } + if (pstr->substream_count == 0) + return -ENOENT; + if (subdevice >= pstr->substream_count) + return -ENXIO; for (substream = pstr->substream; substream; substream = substream->next) if (substream->number == (int)subdevice) break; - if (substream == NULL) { - err = -ENXIO; - goto _error; - } - mutex_lock(&pcm->open_mutex); - err = snd_pcm_info_user(substream, info); - mutex_unlock(&pcm->open_mutex); - _error: - mutex_unlock(®ister_mutex); - return err; + if (substream == NULL) + return -ENXIO; + guard(mutex)(&pcm->open_mutex); + return snd_pcm_info_user(substream, info); } case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: { @@ -225,9 +211,11 @@ static const char * const snd_pcm_format_names[] = { */ const char *snd_pcm_format_name(snd_pcm_format_t format) { - if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) + unsigned int format_num = (__force unsigned int)format; + + if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num]) return "Unknown"; - return snd_pcm_format_names[(__force unsigned int)format]; + return snd_pcm_format_names[format_num]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -340,7 +328,7 @@ static const char *snd_pcm_oss_format_name(int format) static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, struct snd_info_buffer *buffer) { - struct snd_pcm_info *info; + struct snd_pcm_info *info __free(kfree) = NULL; int err; if (! substream) @@ -353,7 +341,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, err = snd_pcm_info(substream, info); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - kfree(info); return; } snd_iprintf(buffer, "card: %d\n", info->card); @@ -367,7 +354,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); - kfree(info); } static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, @@ -389,15 +375,15 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } if (runtime->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - goto unlock; + return; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); @@ -416,8 +402,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif - unlock: - mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, @@ -426,15 +410,15 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } if (runtime->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - goto unlock; + return; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); @@ -444,8 +428,6 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); - unlock: - mutex_unlock(&substream->pcm->open_mutex); } static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, @@ -456,17 +438,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, struct snd_pcm_status64 status; int err; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); - goto unlock; + return; } memset(&status, 0, sizeof(status)); err = snd_pcm_status64(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); - goto unlock; + return; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); @@ -480,8 +462,6 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, snd_iprintf(buffer, "-----\n"); snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); - unlock: - mutex_unlock(&substream->pcm->open_mutex); } #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -1009,9 +989,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) kfree(runtime->hw_constraints.rules); /* Avoid concurrent access to runtime via PCM timer interface */ if (substream->timer) { - spin_lock_irq(&substream->timer->lock); - substream->runtime = NULL; - spin_unlock_irq(&substream->timer->lock); + scoped_guard(spinlock_irq, &substream->timer->lock) + substream->runtime = NULL; } else { substream->runtime = NULL; } @@ -1068,10 +1047,10 @@ static int snd_pcm_dev_register(struct snd_device *device) return -ENXIO; pcm = device->device_data; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); err = snd_pcm_add(pcm); if (err) - goto unlock; + return err; for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; if (pcm->streams[cidx].substream == NULL) @@ -1090,7 +1069,7 @@ static int snd_pcm_dev_register(struct snd_device *device) pcm->streams[cidx].dev); if (err < 0) { list_del_init(&pcm->list); - goto unlock; + return err; } for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) @@ -1098,9 +1077,6 @@ static int snd_pcm_dev_register(struct snd_device *device) } pcm_call_notify(pcm, n_register); - - unlock: - mutex_unlock(®ister_mutex); return err; } @@ -1110,8 +1086,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) struct snd_pcm_substream *substream; int cidx; - mutex_lock(®ister_mutex); - mutex_lock(&pcm->open_mutex); + guard(mutex)(®ister_mutex); + guard(mutex)(&pcm->open_mutex); wake_up(&pcm->open_wait); list_del_init(&pcm->list); @@ -1138,8 +1114,6 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) snd_unregister_device(pcm->streams[cidx].dev); free_chmap(&pcm->streams[cidx]); } - mutex_unlock(&pcm->open_mutex); - mutex_unlock(®ister_mutex); return 0; } @@ -1164,7 +1138,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) !notify->n_unregister || !notify->n_disconnect)) return -EINVAL; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); if (nfree) { list_del(¬ify->list); list_for_each_entry(pcm, &snd_pcm_devices, list) @@ -1174,7 +1148,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) list_for_each_entry(pcm, &snd_pcm_devices, list) notify->n_register(pcm); } - mutex_unlock(®ister_mutex); return 0; } EXPORT_SYMBOL(snd_pcm_notify); @@ -1190,7 +1163,7 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry, { struct snd_pcm *pcm; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_for_each_entry(pcm, &snd_pcm_devices, list) { snd_iprintf(buffer, "%02i-%02i: %s : %s", pcm->card->number, pcm->device, pcm->id, pcm->name); @@ -1202,7 +1175,6 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); snd_iprintf(buffer, "\n"); } - mutex_unlock(®ister_mutex); } static struct snd_info_entry *snd_pcm_proc_entry; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index c96483091f30..a42ec7f5a1da 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -235,7 +235,7 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, int refine, struct snd_pcm_hw_params32 __user *data32) { - struct snd_pcm_hw_params *data; + struct snd_pcm_hw_params *data __free(kfree) = NULL; struct snd_pcm_runtime *runtime; int err; @@ -248,34 +248,28 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, return -ENOMEM; /* only fifo_size (RO from userspace) is different, so just copy all */ - if (copy_from_user(data, data32, sizeof(*data32))) { - err = -EFAULT; - goto error; - } + if (copy_from_user(data, data32, sizeof(*data32))) + return -EFAULT; if (refine) { err = snd_pcm_hw_refine(substream, data); if (err < 0) - goto error; + return err; err = fixup_unreferenced_params(substream, data); } else { err = snd_pcm_hw_params(substream, data); } if (err < 0) - goto error; + return err; if (copy_to_user(data32, data, sizeof(*data32)) || - put_user(data->fifo_size, &data32->fifo_size)) { - err = -EFAULT; - goto error; - } + put_user(data->fifo_size, &data32->fifo_size)) + return -EFAULT; if (! refine) { unsigned int new_boundary = recalculate_boundary(runtime); if (new_boundary) runtime->boundary = new_boundary; } - error: - kfree(data); return err; } @@ -338,7 +332,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, compat_caddr_t buf; compat_caddr_t __user *bufptr; u32 frames; - void __user **bufs; + void __user **bufs __free(kfree) = NULL; int err, ch, i; if (! substream->runtime) @@ -360,10 +354,8 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, return -ENOMEM; for (i = 0; i < ch; i++) { u32 ptr; - if (get_user(ptr, bufptr)) { - kfree(bufs); + if (get_user(ptr, bufptr)) return -EFAULT; - } bufs[i] = compat_ptr(ptr); bufptr++; } @@ -373,9 +365,8 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, err = snd_pcm_lib_readv(substream, bufs, frames); if (err >= 0) { if (put_user(err, &data32->result)) - err = -EFAULT; + return -EFAULT; } - kfree(bufs); return err; } @@ -441,22 +432,22 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, boundary = recalculate_boundary(runtime); if (!boundary) boundary = 0x7fffffff; - snd_pcm_stream_lock_irq(substream); - /* FIXME: we should consider the boundary for the sync from app */ - if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) - control->appl_ptr = scontrol.appl_ptr; - else - scontrol.appl_ptr = control->appl_ptr % boundary; - if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = scontrol.avail_min; - else - scontrol.avail_min = control->avail_min; - sstatus.state = status->state; - sstatus.hw_ptr = status->hw_ptr % boundary; - sstatus.tstamp = status->tstamp; - sstatus.suspended_state = status->suspended_state; - sstatus.audio_tstamp = status->audio_tstamp; - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) { + /* FIXME: we should consider the boundary for the sync from app */ + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + control->appl_ptr = scontrol.appl_ptr; + else + scontrol.appl_ptr = control->appl_ptr % boundary; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; + else + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr % boundary; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; + } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || @@ -519,26 +510,24 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream, if (err < 0) return err; } - snd_pcm_stream_lock_irq(substream); - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr); - if (err < 0) { - snd_pcm_stream_unlock_irq(substream); - return err; + scoped_guard(pcm_stream_lock_irq, substream) { + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr); + if (err < 0) + return err; + } else { + sync_cp->appl_ptr = control->appl_ptr; } - } else { - sync_cp->appl_ptr = control->appl_ptr; + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = sync_cp->avail_min; + else + sync_cp->avail_min = control->avail_min; + sync_ptr.s.status.state = status->state; + sync_ptr.s.status.hw_ptr = status->hw_ptr; + sync_ptr.s.status.tstamp = status->tstamp; + sync_ptr.s.status.suspended_state = status->suspended_state; + sync_ptr.s.status.audio_tstamp = status->audio_tstamp; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = sync_cp->avail_min; - else - sync_cp->avail_min = control->avail_min; - sync_ptr.s.status.state = status->state; - sync_ptr.s.status.hw_ptr = status->hw_ptr; - sync_ptr.s.status.tstamp = status->tstamp; - sync_ptr.s.status.suspended_state = status->suspended_state; - sync_ptr.s.status.audio_tstamp = status->audio_tstamp; - snd_pcm_stream_unlock_irq(substream); if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 41103e5c43ce..6f73b3c2c205 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1744,8 +1744,8 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; - snd_pcm_stream_lock_irqsave(substream, flags); + + guard(pcm_stream_lock_irqsave)(substream); if (snd_pcm_running(substream) && snd_pcm_update_hw_ptr(substream) >= 0) runtime->status->hw_ptr %= runtime->buffer_size; @@ -1753,7 +1753,6 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, runtime->status->hw_ptr = 0; runtime->hw_ptr_wrap = 0; } - snd_pcm_stream_unlock_irqrestore(substream, flags); return 0; } @@ -1899,14 +1898,11 @@ EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock); */ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) { - unsigned long flags; - if (snd_BUG_ON(!substream)) return; - snd_pcm_stream_lock_irqsave(substream, flags); + guard(pcm_stream_lock_irqsave)(substream); snd_pcm_period_elapsed_under_stream_lock(substream); - snd_pcm_stream_unlock_irqrestore(substream, flags); } EXPORT_SYMBOL(snd_pcm_period_elapsed); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index a0b951471699..506386959f08 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -38,17 +38,15 @@ static void __update_allocated_size(struct snd_card *card, ssize_t bytes) static void update_allocated_size(struct snd_card *card, ssize_t bytes) { - mutex_lock(&card->memory_mutex); + guard(mutex)(&card->memory_mutex); __update_allocated_size(card, bytes); - mutex_unlock(&card->memory_mutex); } static void decrease_allocated_size(struct snd_card *card, size_t bytes) { - mutex_lock(&card->memory_mutex); + guard(mutex)(&card->memory_mutex); WARN_ON(card->total_pcm_alloc_bytes < bytes); __update_allocated_size(card, -(ssize_t)bytes); - mutex_unlock(&card->memory_mutex); } static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, @@ -58,14 +56,12 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, int err; /* check and reserve the requested size */ - mutex_lock(&card->memory_mutex); - if (max_alloc_per_card && - card->total_pcm_alloc_bytes + size > max_alloc_per_card) { - mutex_unlock(&card->memory_mutex); - return -ENOMEM; + scoped_guard(mutex, &card->memory_mutex) { + if (max_alloc_per_card && + card->total_pcm_alloc_bytes + size > max_alloc_per_card) + return -ENOMEM; + __update_allocated_size(card, size); } - __update_allocated_size(card, size); - mutex_unlock(&card->memory_mutex); if (str == SNDRV_PCM_STREAM_PLAYBACK) dir = DMA_TO_DEVICE; @@ -191,20 +187,20 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, size_t size; struct snd_dma_buffer new_dmab; - mutex_lock(&substream->pcm->open_mutex); + guard(mutex)(&substream->pcm->open_mutex); if (substream->runtime) { buffer->error = -EBUSY; - goto unlock; + return; } if (!snd_info_get_line(buffer, line, sizeof(line))) { snd_info_get_str(str, line, sizeof(str)); size = simple_strtoul(str, NULL, 10) * 1024; if ((size != 0 && size < 8192) || size > substream->dma_max) { buffer->error = -EINVAL; - goto unlock; + return; } if (substream->dma_buffer.bytes == size) - goto unlock; + return; memset(&new_dmab, 0, sizeof(new_dmab)); new_dmab.dev = substream->dma_buffer.dev; if (size > 0) { @@ -218,7 +214,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, substream->pcm->card->number, substream->pcm->device, substream->stream ? 'c' : 'p', substream->number, substream->pcm->name, size); - goto unlock; + return; } substream->buffer_bytes_max = size; } else { @@ -230,8 +226,6 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, } else { buffer->error = -EINVAL; } - unlock: - mutex_unlock(&substream->pcm->open_mutex); } static inline void preallocate_info_init(struct snd_pcm_substream *substream) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 21baf6bf7e25..0b76e76823d2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -236,7 +236,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) int snd_pcm_info_user(struct snd_pcm_substream *substream, struct snd_pcm_info __user * _info) { - struct snd_pcm_info *info; + struct snd_pcm_info *info __free(kfree) = NULL; int err; info = kmalloc(sizeof(*info), GFP_KERNEL); @@ -247,7 +247,6 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, if (copy_to_user(_info, info, sizeof(*info))) err = -EFAULT; } - kfree(info); return err; } @@ -359,7 +358,7 @@ static int constrain_params_by_rules(struct snd_pcm_substream *substream, struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; unsigned int k; - unsigned int *rstamps; + unsigned int *rstamps __free(kfree) = NULL; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp; struct snd_pcm_hw_rule *r; @@ -435,10 +434,8 @@ retry: } changed = r->func(params, r); - if (changed < 0) { - err = changed; - goto out; - } + if (changed < 0) + return changed; /* * When the parameter is changed, notify it to the caller @@ -469,8 +466,6 @@ retry: if (again) goto retry; - out: - kfree(rstamps); return err; } @@ -576,26 +571,24 @@ EXPORT_SYMBOL(snd_pcm_hw_refine); static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { - struct snd_pcm_hw_params *params; + struct snd_pcm_hw_params *params __free(kfree) = NULL; int err; params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(params); + return PTR_ERR(no_free_ptr(params)); err = snd_pcm_hw_refine(substream, params); if (err < 0) - goto end; + return err; err = fixup_unreferenced_params(substream, params); if (err < 0) - goto end; + return err; if (copy_to_user(_params, params, sizeof(*params))) - err = -EFAULT; -end: - kfree(params); - return err; + return -EFAULT; + return 0; } static int period_to_usecs(struct snd_pcm_runtime *runtime) @@ -616,10 +609,9 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) static void snd_pcm_set_state(struct snd_pcm_substream *substream, snd_pcm_state_t state) { - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED) __snd_pcm_set_state(substream->runtime, state); - snd_pcm_stream_unlock_irq(substream); } static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, @@ -745,20 +737,20 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, err = snd_pcm_buffer_access_lock(runtime); if (err < 0) return err; - snd_pcm_stream_lock_irq(substream); - switch (runtime->state) { - case SNDRV_PCM_STATE_OPEN: - case SNDRV_PCM_STATE_SETUP: - case SNDRV_PCM_STATE_PREPARED: - if (!is_oss_stream(substream) && - atomic_read(&substream->mmap_count)) + scoped_guard(pcm_stream_lock_irq, substream) { + switch (runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + if (!is_oss_stream(substream) && + atomic_read(&substream->mmap_count)) + err = -EBADFD; + break; + default: err = -EBADFD; - break; - default: - err = -EBADFD; - break; + break; + } } - snd_pcm_stream_unlock_irq(substream); if (err) goto unlock; @@ -869,21 +861,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { - struct snd_pcm_hw_params *params; + struct snd_pcm_hw_params *params __free(kfree) = NULL; int err; params = memdup_user(_params, sizeof(*params)); if (IS_ERR(params)) - return PTR_ERR(params); + return PTR_ERR(no_free_ptr(params)); err = snd_pcm_hw_params(substream, params); if (err < 0) - goto end; + return err; if (copy_to_user(_params, params, sizeof(*params))) - err = -EFAULT; -end: - kfree(params); + return -EFAULT; return err; } @@ -910,18 +900,18 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) result = snd_pcm_buffer_access_lock(runtime); if (result < 0) return result; - snd_pcm_stream_lock_irq(substream); - switch (runtime->state) { - case SNDRV_PCM_STATE_SETUP: - case SNDRV_PCM_STATE_PREPARED: - if (atomic_read(&substream->mmap_count)) + scoped_guard(pcm_stream_lock_irq, substream) { + switch (runtime->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + if (atomic_read(&substream->mmap_count)) + result = -EBADFD; + break; + default: result = -EBADFD; - break; - default: - result = -EBADFD; - break; + break; + } } - snd_pcm_stream_unlock_irq(substream); if (result) goto unlock; result = do_hw_free(substream); @@ -941,12 +931,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - snd_pcm_stream_lock_irq(substream); - if (runtime->state == SNDRV_PCM_STATE_OPEN) { - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + scoped_guard(pcm_stream_lock_irq, substream) { + if (runtime->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; } - snd_pcm_stream_unlock_irq(substream); if (params->tstamp_mode < 0 || params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) @@ -966,24 +954,24 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, return -EINVAL; } err = 0; - snd_pcm_stream_lock_irq(substream); - runtime->tstamp_mode = params->tstamp_mode; - if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12)) - runtime->tstamp_type = params->tstamp_type; - runtime->period_step = params->period_step; - runtime->control->avail_min = params->avail_min; - runtime->start_threshold = params->start_threshold; - runtime->stop_threshold = params->stop_threshold; - runtime->silence_threshold = params->silence_threshold; - runtime->silence_size = params->silence_size; - params->boundary = runtime->boundary; - if (snd_pcm_running(substream)) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, ULONG_MAX); - err = snd_pcm_update_state(substream, runtime); + scoped_guard(pcm_stream_lock_irq, substream) { + runtime->tstamp_mode = params->tstamp_mode; + if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12)) + runtime->tstamp_type = params->tstamp_type; + runtime->period_step = params->period_step; + runtime->control->avail_min = params->avail_min; + runtime->start_threshold = params->start_threshold; + runtime->stop_threshold = params->stop_threshold; + runtime->silence_threshold = params->silence_threshold; + runtime->silence_size = params->silence_size; + params->boundary = runtime->boundary; + if (snd_pcm_running(substream)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream, ULONG_MAX); + err = snd_pcm_update_state(substream, runtime); + } } - snd_pcm_stream_unlock_irq(substream); return err; } @@ -1017,7 +1005,7 @@ int snd_pcm_status64(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data, &runtime->audio_tstamp_config); @@ -1038,7 +1026,7 @@ int snd_pcm_status64(struct snd_pcm_substream *substream, status->state = runtime->state; status->suspended_state = runtime->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) - goto _end; + return 0; status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec; status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec; if (snd_pcm_running(substream)) { @@ -1083,8 +1071,6 @@ int snd_pcm_status64(struct snd_pcm_substream *substream, status->overrange = runtime->overrange; runtime->avail_max = 0; runtime->overrange = 0; - _end: - snd_pcm_stream_unlock_irq(substream); return 0; } @@ -1169,12 +1155,10 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream, channel = info->channel; runtime = substream->runtime; - snd_pcm_stream_lock_irq(substream); - if (runtime->state == SNDRV_PCM_STATE_OPEN) { - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + scoped_guard(pcm_stream_lock_irq, substream) { + if (runtime->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; } - snd_pcm_stream_unlock_irq(substream); if (channel >= runtime->channels) return -EINVAL; memset(info, 0, sizeof(*info)); @@ -1395,12 +1379,8 @@ static int snd_pcm_action_lock_irq(const struct action_ops *ops, struct snd_pcm_substream *substream, snd_pcm_state_t state) { - int res; - - snd_pcm_stream_lock_irq(substream); - res = snd_pcm_action(ops, substream, state); - snd_pcm_stream_unlock_irq(substream); - return res; + guard(pcm_stream_lock_irq)(substream); + return snd_pcm_action(ops, substream, state); } /* @@ -1412,17 +1392,15 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops, int res; /* Guarantee the group members won't change during non-atomic action */ - down_read(&snd_pcm_link_rwsem); + guard(rwsem_read)(&snd_pcm_link_rwsem); res = snd_pcm_buffer_access_lock(substream->runtime); if (res < 0) - goto unlock; + return res; if (snd_pcm_stream_linked(substream)) res = snd_pcm_action_group(ops, substream, state, false); else res = snd_pcm_action_single(ops, substream, state); snd_pcm_buffer_access_unlock(substream->runtime); - unlock: - up_read(&snd_pcm_link_rwsem); return res; } @@ -1592,12 +1570,9 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream) */ int snd_pcm_stop_xrun(struct snd_pcm_substream *substream) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(substream, flags); + guard(pcm_stream_lock_irqsave)(substream); if (substream->runtime && snd_pcm_running(substream)) __snd_pcm_xrun(substream); - snd_pcm_stream_unlock_irqrestore(substream, flags); return 0; } EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun); @@ -1749,14 +1724,9 @@ static const struct action_ops snd_pcm_action_suspend = { */ static int snd_pcm_suspend(struct snd_pcm_substream *substream) { - int err; - unsigned long flags; - - snd_pcm_stream_lock_irqsave(substream, flags); - err = snd_pcm_action(&snd_pcm_action_suspend, substream, - ACTION_ARG_IGNORE); - snd_pcm_stream_unlock_irqrestore(substream, flags); - return err; + guard(pcm_stream_lock_irqsave)(substream); + return snd_pcm_action(&snd_pcm_action_suspend, substream, + ACTION_ARG_IGNORE); } /** @@ -1872,22 +1842,17 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream) static int snd_pcm_xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - int result; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); switch (runtime->state) { case SNDRV_PCM_STATE_XRUN: - result = 0; /* already there */ - break; + return 0; /* already there */ case SNDRV_PCM_STATE_RUNNING: __snd_pcm_xrun(substream); - result = 0; - break; + return 0; default: - result = -EBADFD; + return -EBADFD; } - snd_pcm_stream_unlock_irq(substream); - return result; } /* @@ -1916,13 +1881,12 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); runtime->hw_ptr_base = 0; runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; - snd_pcm_stream_unlock_irq(substream); return 0; } @@ -1930,12 +1894,11 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream, snd_pcm_state_t state) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); runtime->control->appl_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - snd_pcm_stream_unlock_irq(substream); } static const struct action_ops snd_pcm_action_reset = { @@ -2011,16 +1974,16 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, else f_flags = substream->f_flags; - snd_pcm_stream_lock_irq(substream); - switch (substream->runtime->state) { - case SNDRV_PCM_STATE_PAUSED: - snd_pcm_pause(substream, false); - fallthrough; - case SNDRV_PCM_STATE_SUSPENDED: - snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); - break; + scoped_guard(pcm_stream_lock_irq, substream) { + switch (substream->runtime->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, false); + fallthrough; + case SNDRV_PCM_STATE_SUSPENDED: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } } - snd_pcm_stream_unlock_irq(substream); return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, @@ -2237,14 +2200,13 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) runtime->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); /* resume pause */ if (runtime->state == SNDRV_PCM_STATE_PAUSED) snd_pcm_pause(substream, false); snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); /* runtime->control->appl_ptr = runtime->status->hw_ptr; */ - snd_pcm_stream_unlock_irq(substream); return result; } @@ -2273,53 +2235,44 @@ static bool is_pcm_file(struct file *file) */ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) { - int res = 0; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; - struct snd_pcm_group *group, *target_group; + struct snd_pcm_group *group __free(kfree) = NULL; + struct snd_pcm_group *target_group; bool nonatomic = substream->pcm->nonatomic; - struct fd f = fdget(fd); + CLASS(fd, f)(fd); if (!f.file) return -EBADFD; - if (!is_pcm_file(f.file)) { - res = -EBADFD; - goto _badf; - } + if (!is_pcm_file(f.file)) + return -EBADFD; + pcm_file = f.file->private_data; substream1 = pcm_file->substream; - if (substream == substream1) { - res = -EINVAL; - goto _badf; - } + if (substream == substream1) + return -EINVAL; group = kzalloc(sizeof(*group), GFP_KERNEL); - if (!group) { - res = -ENOMEM; - goto _nolock; - } + if (!group) + return -ENOMEM; snd_pcm_group_init(group); - down_write(&snd_pcm_link_rwsem); + guard(rwsem_write)(&snd_pcm_link_rwsem); if (substream->runtime->state == SNDRV_PCM_STATE_OPEN || substream->runtime->state != substream1->runtime->state || - substream->pcm->nonatomic != substream1->pcm->nonatomic) { - res = -EBADFD; - goto _end; - } - if (snd_pcm_stream_linked(substream1)) { - res = -EALREADY; - goto _end; - } + substream->pcm->nonatomic != substream1->pcm->nonatomic) + return -EBADFD; + if (snd_pcm_stream_linked(substream1)) + return -EALREADY; - snd_pcm_stream_lock_irq(substream); - if (!snd_pcm_stream_linked(substream)) { - snd_pcm_group_assign(substream, group); - group = NULL; /* assigned, don't free this one below */ + scoped_guard(pcm_stream_lock_irq, substream) { + if (!snd_pcm_stream_linked(substream)) { + snd_pcm_group_assign(substream, group); + group = NULL; /* assigned, don't free this one below */ + } + target_group = substream->group; } - target_group = substream->group; - snd_pcm_stream_unlock_irq(substream); snd_pcm_group_lock_irq(target_group, nonatomic); snd_pcm_stream_lock_nested(substream1); @@ -2327,13 +2280,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) refcount_inc(&target_group->refs); snd_pcm_stream_unlock(substream1); snd_pcm_group_unlock_irq(target_group, nonatomic); - _end: - up_write(&snd_pcm_link_rwsem); - _nolock: - kfree(group); - _badf: - fdput(f); - return res; + return 0; } static void relink_to_local(struct snd_pcm_substream *substream) @@ -2348,14 +2295,11 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) struct snd_pcm_group *group; bool nonatomic = substream->pcm->nonatomic; bool do_free = false; - int res = 0; - down_write(&snd_pcm_link_rwsem); + guard(rwsem_write)(&snd_pcm_link_rwsem); - if (!snd_pcm_stream_linked(substream)) { - res = -EALREADY; - goto _end; - } + if (!snd_pcm_stream_linked(substream)) + return -EALREADY; group = substream->group; snd_pcm_group_lock_irq(group, nonatomic); @@ -2374,10 +2318,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) snd_pcm_group_unlock_irq(group, nonatomic); if (do_free) kfree(group); - - _end: - up_write(&snd_pcm_link_rwsem); - return res; + return 0; } /* @@ -2950,10 +2891,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file) /* block until the device gets woken up as it may touch the hardware */ snd_power_wait(pcm->card); - mutex_lock(&pcm->open_mutex); - snd_pcm_release_substream(substream); - kfree(pcm_file); - mutex_unlock(&pcm->open_mutex); + scoped_guard(mutex, &pcm->open_mutex) { + snd_pcm_release_substream(substream); + kfree(pcm_file); + } wake_up(&pcm->open_wait); module_put(pcm->card->module); snd_card_file_remove(pcm->card, file); @@ -3037,12 +2978,12 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, if (frames == 0) return 0; - snd_pcm_stream_lock_irq(substream); - ret = do_pcm_hwsync(substream); - if (!ret) - ret = rewind_appl_ptr(substream, frames, - snd_pcm_hw_avail(substream)); - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) { + ret = do_pcm_hwsync(substream); + if (!ret) + ret = rewind_appl_ptr(substream, frames, + snd_pcm_hw_avail(substream)); + } if (ret >= 0) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; @@ -3056,12 +2997,12 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream, if (frames == 0) return 0; - snd_pcm_stream_lock_irq(substream); - ret = do_pcm_hwsync(substream); - if (!ret) - ret = forward_appl_ptr(substream, frames, - snd_pcm_avail(substream)); - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) { + ret = do_pcm_hwsync(substream); + if (!ret) + ret = forward_appl_ptr(substream, frames, + snd_pcm_avail(substream)); + } if (ret >= 0) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; @@ -3072,11 +3013,11 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, { int err; - snd_pcm_stream_lock_irq(substream); - err = do_pcm_hwsync(substream); - if (delay && !err) - *delay = snd_pcm_calc_delay(substream); - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) { + err = do_pcm_hwsync(substream); + if (delay && !err) + *delay = snd_pcm_calc_delay(substream); + } snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); return err; @@ -3108,27 +3049,25 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, if (err < 0) return err; } - snd_pcm_stream_lock_irq(substream); - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = pcm_lib_apply_appl_ptr(substream, - sync_ptr.c.control.appl_ptr); - if (err < 0) { - snd_pcm_stream_unlock_irq(substream); - return err; + scoped_guard(pcm_stream_lock_irq, substream) { + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, + sync_ptr.c.control.appl_ptr); + if (err < 0) + return err; + } else { + sync_ptr.c.control.appl_ptr = control->appl_ptr; } - } else { - sync_ptr.c.control.appl_ptr = control->appl_ptr; + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = sync_ptr.c.control.avail_min; + else + sync_ptr.c.control.avail_min = control->avail_min; + sync_ptr.s.status.state = status->state; + sync_ptr.s.status.hw_ptr = status->hw_ptr; + sync_ptr.s.status.tstamp = status->tstamp; + sync_ptr.s.status.suspended_state = status->suspended_state; + sync_ptr.s.status.audio_tstamp = status->audio_tstamp; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = sync_ptr.c.control.avail_min; - else - sync_ptr.c.control.avail_min = control->avail_min; - sync_ptr.s.status.state = status->state; - sync_ptr.s.status.hw_ptr = status->hw_ptr; - sync_ptr.s.status.tstamp = status->tstamp; - sync_ptr.s.status.suspended_state = status->suspended_state; - sync_ptr.s.status.audio_tstamp = status->audio_tstamp; - snd_pcm_stream_unlock_irq(substream); if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) @@ -3206,27 +3145,25 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, boundary = recalculate_boundary(runtime); if (! boundary) boundary = 0x7fffffff; - snd_pcm_stream_lock_irq(substream); - /* FIXME: we should consider the boundary for the sync from app */ - if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = pcm_lib_apply_appl_ptr(substream, - scontrol.appl_ptr); - if (err < 0) { - snd_pcm_stream_unlock_irq(substream); - return err; - } - } else - scontrol.appl_ptr = control->appl_ptr % boundary; - if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = scontrol.avail_min; - else - scontrol.avail_min = control->avail_min; - sstatus.state = status->state; - sstatus.hw_ptr = status->hw_ptr % boundary; - sstatus.tstamp = status->tstamp; - sstatus.suspended_state = status->suspended_state; - sstatus.audio_tstamp = status->audio_tstamp; - snd_pcm_stream_unlock_irq(substream); + scoped_guard(pcm_stream_lock_irq, substream) { + /* FIXME: we should consider the boundary for the sync from app */ + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, + scontrol.appl_ptr); + if (err < 0) + return err; + } else + scontrol.appl_ptr = control->appl_ptr % boundary; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; + else + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr % boundary; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; + } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || @@ -3284,7 +3221,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, { struct snd_xfern xfern; struct snd_pcm_runtime *runtime = substream->runtime; - void *bufs; + void *bufs __free(kfree) = NULL; snd_pcm_sframes_t result; if (runtime->state == SNDRV_PCM_STATE_OPEN) @@ -3298,12 +3235,11 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels); if (IS_ERR(bufs)) - return PTR_ERR(bufs); + return PTR_ERR(no_free_ptr(bufs)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) result = snd_pcm_lib_writev(substream, bufs, xfern.frames); else result = snd_pcm_lib_readv(substream, bufs, xfern.frames); - kfree(bufs); if (put_user(result, &_xfern->result)) return -EFAULT; return result < 0 ? result : 0; @@ -3571,7 +3507,7 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) struct snd_pcm_runtime *runtime; snd_pcm_sframes_t result; unsigned long i; - void __user **bufs; + void __user **bufs __free(kfree) = NULL; snd_pcm_uframes_t frames; const struct iovec *iov = iter_iov(to); @@ -3600,7 +3536,6 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) result = snd_pcm_lib_readv(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); - kfree(bufs); return result; } @@ -3611,7 +3546,7 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) struct snd_pcm_runtime *runtime; snd_pcm_sframes_t result; unsigned long i; - void __user **bufs; + void __user **bufs __free(kfree) = NULL; snd_pcm_uframes_t frames; const struct iovec *iov = iter_iov(from); @@ -3639,7 +3574,6 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) result = snd_pcm_lib_writev(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); - kfree(bufs); return result; } @@ -3668,7 +3602,7 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait) poll_wait(file, &runtime->sleep, wait); mask = 0; - snd_pcm_stream_lock_irq(substream); + guard(pcm_stream_lock_irq)(substream); avail = snd_pcm_avail(substream); switch (runtime->state) { case SNDRV_PCM_STATE_RUNNING: @@ -3688,7 +3622,6 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait) mask = ok | EPOLLERR; break; } - snd_pcm_stream_unlock_irq(substream); return mask; } @@ -4081,8 +4014,8 @@ static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *opara static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams) { - struct snd_pcm_hw_params *params; - struct snd_pcm_hw_params_old *oparams = NULL; + struct snd_pcm_hw_params *params __free(kfree) = NULL; + struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL; int err; params = kmalloc(sizeof(*params), GFP_KERNEL); @@ -4090,34 +4023,28 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, return -ENOMEM; oparams = memdup_user(_oparams, sizeof(*oparams)); - if (IS_ERR(oparams)) { - err = PTR_ERR(oparams); - goto out; - } + if (IS_ERR(oparams)) + return PTR_ERR(no_free_ptr(oparams)); snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_refine(substream, params); if (err < 0) - goto out_old; + return err; err = fixup_unreferenced_params(substream, params); if (err < 0) - goto out_old; + return err; snd_pcm_hw_convert_to_old_params(oparams, params); if (copy_to_user(_oparams, oparams, sizeof(*oparams))) - err = -EFAULT; -out_old: - kfree(oparams); -out: - kfree(params); - return err; + return -EFAULT; + return 0; } static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams) { - struct snd_pcm_hw_params *params; - struct snd_pcm_hw_params_old *oparams = NULL; + struct snd_pcm_hw_params *params __free(kfree) = NULL; + struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL; int err; params = kmalloc(sizeof(*params), GFP_KERNEL); @@ -4125,24 +4052,18 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, return -ENOMEM; oparams = memdup_user(_oparams, sizeof(*oparams)); - if (IS_ERR(oparams)) { - err = PTR_ERR(oparams); - goto out; - } + if (IS_ERR(oparams)) + return PTR_ERR(no_free_ptr(oparams)); snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_params(substream, params); if (err < 0) - goto out_old; + return err; snd_pcm_hw_convert_to_old_params(oparams, params); if (copy_to_user(_oparams, oparams, sizeof(*oparams))) - err = -EFAULT; -out_old: - kfree(oparams); -out: - kfree(params); - return err; + return -EFAULT; + return 0; } #endif /* CONFIG_SND_SUPPORT_OLD_API */ diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 1431cb997808..7accf9a1ddf4 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -105,13 +105,8 @@ static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime) static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream) { - unsigned long flags; - bool ready; - - spin_lock_irqsave(&substream->lock, flags); - ready = __snd_rawmidi_ready(substream->runtime); - spin_unlock_irqrestore(&substream->lock, flags); - return ready; + guard(spinlock_irqsave)(&substream->lock); + return __snd_rawmidi_ready(substream->runtime); } static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream, @@ -238,12 +233,9 @@ static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime, static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream, bool is_input) { - unsigned long flags; - - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); if (substream->opened && substream->runtime) __reset_runtime_ptrs(substream->runtime, is_input); - spin_unlock_irqrestore(&substream->lock, flags); } int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) @@ -260,33 +252,29 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) long timeout; struct snd_rawmidi_runtime *runtime; - spin_lock_irq(&substream->lock); - runtime = substream->runtime; - if (!substream->opened || !runtime || !runtime->buffer) { - err = -EINVAL; - } else { + scoped_guard(spinlock_irq, &substream->lock) { + runtime = substream->runtime; + if (!substream->opened || !runtime || !runtime->buffer) + return -EINVAL; snd_rawmidi_buffer_ref(runtime); runtime->drain = 1; } - spin_unlock_irq(&substream->lock); - if (err < 0) - return err; timeout = wait_event_interruptible_timeout(runtime->sleep, (runtime->avail >= runtime->buffer_size), 10*HZ); - spin_lock_irq(&substream->lock); - if (signal_pending(current)) - err = -ERESTARTSYS; - if (runtime->avail < runtime->buffer_size && !timeout) { - rmidi_warn(substream->rmidi, - "rawmidi drain error (avail = %li, buffer_size = %li)\n", - (long)runtime->avail, (long)runtime->buffer_size); - err = -EIO; + scoped_guard(spinlock_irq, &substream->lock) { + if (signal_pending(current)) + err = -ERESTARTSYS; + if (runtime->avail < runtime->buffer_size && !timeout) { + rmidi_warn(substream->rmidi, + "rawmidi drain error (avail = %li, buffer_size = %li)\n", + (long)runtime->avail, (long)runtime->buffer_size); + err = -EIO; + } + runtime->drain = 0; } - runtime->drain = 0; - spin_unlock_irq(&substream->lock); if (err != -ERESTARTSYS) { /* we need wait a while to make sure that Tx FIFOs are empty */ @@ -297,9 +285,8 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) snd_rawmidi_drop_output(substream); } - spin_lock_irq(&substream->lock); - snd_rawmidi_buffer_unref(runtime); - spin_unlock_irq(&substream->lock); + scoped_guard(spinlock_irq, &substream->lock) + snd_rawmidi_buffer_unref(runtime); return err; } @@ -363,14 +350,13 @@ static int open_substream(struct snd_rawmidi *rmidi, snd_rawmidi_runtime_free(substream); return err; } - spin_lock_irq(&substream->lock); + guard(spinlock_irq)(&substream->lock); substream->opened = 1; substream->active_sensing = 0; if (mode & SNDRV_RAWMIDI_LFLG_APPEND) substream->append = 1; substream->pid = get_pid(task_pid(current)); rmidi->streams[substream->stream].substream_opened++; - spin_unlock_irq(&substream->lock); } substream->use_count++; return 0; @@ -433,9 +419,8 @@ int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, if (!try_module_get(rmidi->card->module)) return -ENXIO; - mutex_lock(&rmidi->open_mutex); + guard(mutex)(&rmidi->open_mutex); err = rawmidi_open_priv(rmidi, subdevice, mode, rfile); - mutex_unlock(&rmidi->open_mutex); if (err < 0) module_put(rmidi->card->module); return err; @@ -568,10 +553,10 @@ static void close_substream(struct snd_rawmidi *rmidi, } snd_rawmidi_buffer_ref_sync(substream); } - spin_lock_irq(&substream->lock); - substream->opened = 0; - substream->append = 0; - spin_unlock_irq(&substream->lock); + scoped_guard(spinlock_irq, &substream->lock) { + substream->opened = 0; + substream->append = 0; + } substream->ops->close(substream); if (substream->runtime->private_free) substream->runtime->private_free(substream); @@ -586,7 +571,7 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) struct snd_rawmidi *rmidi; rmidi = rfile->rmidi; - mutex_lock(&rmidi->open_mutex); + guard(mutex)(&rmidi->open_mutex); if (rfile->input) { close_substream(rmidi, rfile->input, 1); rfile->input = NULL; @@ -596,7 +581,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) rfile->output = NULL; } rfile->rmidi = NULL; - mutex_unlock(&rmidi->open_mutex); wake_up(&rmidi->open_wait); } @@ -695,12 +679,8 @@ static int __snd_rawmidi_info_select(struct snd_card *card, int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info) { - int ret; - - mutex_lock(®ister_mutex); - ret = __snd_rawmidi_info_select(card, info); - mutex_unlock(®ister_mutex); - return ret; + guard(mutex)(®ister_mutex); + return __snd_rawmidi_info_select(card, info); } EXPORT_SYMBOL(snd_rawmidi_info_select); @@ -744,9 +724,8 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); if (!newbuf) return -ENOMEM; - spin_lock_irq(&substream->lock); + guard(spinlock_irq)(&substream->lock); if (runtime->buffer_ref) { - spin_unlock_irq(&substream->lock); kvfree(newbuf); return -EBUSY; } @@ -754,7 +733,6 @@ static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; __reset_runtime_ptrs(runtime, is_input); - spin_unlock_irq(&substream->lock); kvfree(oldbuf); } runtime->avail_min = params->avail_min; @@ -767,15 +745,12 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, int err; snd_rawmidi_drain_output(substream); - mutex_lock(&substream->rmidi->open_mutex); + guard(mutex)(&substream->rmidi->open_mutex); if (substream->append && substream->use_count > 1) - err = -EBUSY; - else - err = resize_runtime_buffer(substream, params, false); - + return -EBUSY; + err = resize_runtime_buffer(substream, params, false); if (!err) substream->active_sensing = !params->no_active_sensing; - mutex_unlock(&substream->rmidi->open_mutex); return err; } EXPORT_SYMBOL(snd_rawmidi_output_params); @@ -788,7 +763,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, int err; snd_rawmidi_drain_input(substream); - mutex_lock(&substream->rmidi->open_mutex); + guard(mutex)(&substream->rmidi->open_mutex); if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE) err = -EINVAL; else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW) @@ -802,7 +777,6 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, substream->framing = framing; substream->clock_type = clock_type; } - mutex_unlock(&substream->rmidi->open_mutex); return 0; } EXPORT_SYMBOL(snd_rawmidi_input_params); @@ -814,9 +788,8 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, memset(status, 0, sizeof(*status)); status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; - spin_lock_irq(&substream->lock); + guard(spinlock_irq)(&substream->lock); status->avail = runtime->avail; - spin_unlock_irq(&substream->lock); return 0; } @@ -827,11 +800,10 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream, memset(status, 0, sizeof(*status)); status->stream = SNDRV_RAWMIDI_STREAM_INPUT; - spin_lock_irq(&substream->lock); + guard(spinlock_irq)(&substream->lock); status->avail = runtime->avail; status->xruns = runtime->xruns; runtime->xruns = 0; - spin_unlock_irq(&substream->lock); return 0; } @@ -1025,19 +997,19 @@ static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp, return -EFAULT; if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */ device = SNDRV_RAWMIDI_DEVICES - 1; - mutex_lock(®ister_mutex); - device = device < 0 ? 0 : device + 1; - for (; device < SNDRV_RAWMIDI_DEVICES; device++) { - rmidi = snd_rawmidi_search(card, device); - if (!rmidi) - continue; - is_ump = rawmidi_is_ump(rmidi); - if (find_ump == is_ump) - break; + scoped_guard(mutex, ®ister_mutex) { + device = device < 0 ? 0 : device + 1; + for (; device < SNDRV_RAWMIDI_DEVICES; device++) { + rmidi = snd_rawmidi_search(card, device); + if (!rmidi) + continue; + is_ump = rawmidi_is_ump(rmidi); + if (find_ump == is_ump) + break; + } + if (device == SNDRV_RAWMIDI_DEVICES) + device = -1; } - if (device == SNDRV_RAWMIDI_DEVICES) - device = -1; - mutex_unlock(®ister_mutex); if (put_user(device, argp)) return -EFAULT; return 0; @@ -1050,18 +1022,16 @@ static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd, { struct snd_ump_endpoint_info __user *info = argp; struct snd_rawmidi *rmidi; - int device, ret; + int device; if (get_user(device, &info->device)) return -EFAULT; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); rmidi = snd_rawmidi_search(card, device); if (rmidi && rmidi->ops && rmidi->ops->ioctl) - ret = rmidi->ops->ioctl(rmidi, cmd, argp); + return rmidi->ops->ioctl(rmidi, cmd, argp); else - ret = -ENXIO; - mutex_unlock(®ister_mutex); - return ret; + return -ENXIO; } #endif @@ -1168,27 +1138,23 @@ static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substr int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, const unsigned char *buffer, int count) { - unsigned long flags; struct timespec64 ts64 = get_framing_tstamp(substream); int result = 0, count1; struct snd_rawmidi_runtime *runtime; - spin_lock_irqsave(&substream->lock, flags); - if (!substream->opened) { - result = -EBADFD; - goto unlock; - } + guard(spinlock_irqsave)(&substream->lock); + if (!substream->opened) + return -EBADFD; runtime = substream->runtime; if (!runtime || !runtime->buffer) { rmidi_dbg(substream->rmidi, "snd_rawmidi_receive: input is not active!!!\n"); - result = -EINVAL; - goto unlock; + return -EINVAL; } count = get_aligned_size(runtime, count); if (!count) - goto unlock; + return result; if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { result = receive_with_tstamp_framing(substream, buffer, count, &ts64); @@ -1211,7 +1177,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, count1 = runtime->buffer_size - runtime->avail; count1 = get_aligned_size(runtime, count1); if (!count1) - goto unlock; + return result; memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); runtime->hw_ptr += count1; runtime->hw_ptr %= runtime->buffer_size; @@ -1239,8 +1205,6 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, else if (__snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } - unlock: - spin_unlock_irqrestore(&substream->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_receive); @@ -1362,20 +1326,15 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime; - int result; - unsigned long flags; - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); runtime = substream->runtime; if (!substream->opened || !runtime || !runtime->buffer) { rmidi_dbg(substream->rmidi, "snd_rawmidi_transmit_empty: output is not active!!!\n"); - result = 1; - } else { - result = runtime->avail >= runtime->buffer_size; + return 1; } - spin_unlock_irqrestore(&substream->lock, flags); - return result; + return (runtime->avail >= runtime->buffer_size); } EXPORT_SYMBOL(snd_rawmidi_transmit_empty); @@ -1449,16 +1408,10 @@ static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { - int result; - unsigned long flags; - - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); if (!substream->opened || !substream->runtime) - result = -EBADFD; - else - result = __snd_rawmidi_transmit_peek(substream, buffer, count); - spin_unlock_irqrestore(&substream->lock, flags); - return result; + return -EBADFD; + return __snd_rawmidi_transmit_peek(substream, buffer, count); } EXPORT_SYMBOL(snd_rawmidi_transmit_peek); @@ -1505,16 +1458,10 @@ static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { - int result; - unsigned long flags; - - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); if (!substream->opened || !substream->runtime) - result = -EBADFD; - else - result = __snd_rawmidi_transmit_ack(substream, count); - spin_unlock_irqrestore(&substream->lock, flags); - return result; + return -EBADFD; + return __snd_rawmidi_transmit_ack(substream, count); } EXPORT_SYMBOL(snd_rawmidi_transmit_ack); @@ -1531,21 +1478,13 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { - int result; - unsigned long flags; - - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); if (!substream->opened) - result = -EBADFD; - else { - count = __snd_rawmidi_transmit_peek(substream, buffer, count); - if (count <= 0) - result = count; - else - result = __snd_rawmidi_transmit_ack(substream, count); - } - spin_unlock_irqrestore(&substream->lock, flags); - return result; + return -EBADFD; + count = __snd_rawmidi_transmit_peek(substream, buffer, count); + if (count <= 0) + return count; + return __snd_rawmidi_transmit_ack(substream, count); } EXPORT_SYMBOL(snd_rawmidi_transmit); @@ -1558,17 +1497,15 @@ EXPORT_SYMBOL(snd_rawmidi_transmit); int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime; - unsigned long flags; int count = 0; - spin_lock_irqsave(&substream->lock, flags); + guard(spinlock_irqsave)(&substream->lock); runtime = substream->runtime; if (substream->opened && runtime && runtime->avail < runtime->buffer_size) { count = runtime->buffer_size - runtime->avail; __snd_rawmidi_transmit_ack(substream, count); } - spin_unlock_irqrestore(&substream->lock, flags); return count; } EXPORT_SYMBOL(snd_rawmidi_proceed); @@ -1772,7 +1709,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, rawmidi_is_ump(rmidi) ? "UMP" : "Legacy"); if (rmidi->ops && rmidi->ops->proc_read) rmidi->ops->proc_read(entry, buffer); - mutex_lock(&rmidi->open_mutex); + guard(mutex)(&rmidi->open_mutex); if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { list_for_each_entry(substream, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, @@ -1787,10 +1724,10 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; - spin_lock_irq(&substream->lock); - buffer_size = runtime->buffer_size; - avail = runtime->avail; - spin_unlock_irq(&substream->lock); + scoped_guard(spinlock_irq, &substream->lock) { + buffer_size = runtime->buffer_size; + avail = runtime->avail; + } snd_iprintf(buffer, " Mode : %s\n" " Buffer size : %lu\n" @@ -1814,11 +1751,11 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; - spin_lock_irq(&substream->lock); - buffer_size = runtime->buffer_size; - avail = runtime->avail; - xruns = runtime->xruns; - spin_unlock_irq(&substream->lock); + scoped_guard(spinlock_irq, &substream->lock) { + buffer_size = runtime->buffer_size; + avail = runtime->avail; + xruns = runtime->xruns; + } snd_iprintf(buffer, " Buffer size : %lu\n" " Avail : %lu\n" @@ -1835,7 +1772,6 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, } } } - mutex_unlock(&rmidi->open_mutex); } /* @@ -2024,12 +1960,12 @@ static int snd_rawmidi_dev_register(struct snd_device *device) if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) return -ENOMEM; err = 0; - mutex_lock(®ister_mutex); - if (snd_rawmidi_search(rmidi->card, rmidi->device)) - err = -EBUSY; - else - list_add_tail(&rmidi->list, &snd_rawmidi_devices); - mutex_unlock(®ister_mutex); + scoped_guard(mutex, ®ister_mutex) { + if (snd_rawmidi_search(rmidi->card, rmidi->device)) + err = -EBUSY; + else + list_add_tail(&rmidi->list, &snd_rawmidi_devices); + } if (err < 0) return err; @@ -2102,9 +2038,8 @@ static int snd_rawmidi_dev_register(struct snd_device *device) error_unregister: snd_unregister_device(rmidi->dev); error: - mutex_lock(®ister_mutex); - list_del(&rmidi->list); - mutex_unlock(®ister_mutex); + scoped_guard(mutex, ®ister_mutex) + list_del(&rmidi->list); return err; } @@ -2113,8 +2048,8 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) struct snd_rawmidi *rmidi = device->device_data; int dir; - mutex_lock(®ister_mutex); - mutex_lock(&rmidi->open_mutex); + guard(mutex)(®ister_mutex); + guard(mutex)(&rmidi->open_mutex); wake_up(&rmidi->open_wait); list_del_init(&rmidi->list); for (dir = 0; dir < 2; dir++) { @@ -2140,8 +2075,6 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) } #endif /* CONFIG_SND_OSSEMUL */ snd_unregister_device(rmidi->dev); - mutex_unlock(&rmidi->open_mutex); - mutex_unlock(®ister_mutex); return 0; } diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index c14981daf943..0374bbf51cd4 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -71,7 +71,6 @@ config SND_SEQ_UMP among legacy and UMP clients. config SND_SEQ_UMP_CLIENT - tristate def_tristate SND_UMP endif # SND_SEQUENCER diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index 6c2c4fb9b753..f0e964b19af7 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -163,6 +163,6 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, /* misc. functions for proc interface */ -char *enabled_str(int bool); +char *enabled_str(bool b); #endif /* __SEQ_OSS_DEVICE_H */ diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 42d4e7535a82..676246fa02f1 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -63,20 +63,18 @@ int __init snd_seq_oss_create_client(void) { int rc; - struct snd_seq_port_info *port; + struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback port_callback; port = kzalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - rc = -ENOMEM; - goto __error; - } + if (!port) + return -ENOMEM; /* create ALSA client */ rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, "OSS sequencer"); if (rc < 0) - goto __error; + return rc; system_client = rc; @@ -104,14 +102,11 @@ snd_seq_oss_create_client(void) subs.dest.port = system_port; call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs); } - rc = 0; /* look up midi devices */ schedule_work(&async_lookup_work); - __error: - kfree(port); - return rc; + return 0; } @@ -455,9 +450,9 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp) * misc. functions for proc interface */ char * -enabled_str(int bool) +enabled_str(bool b) { - return bool ? "enabled" : "disabled"; + return b ? "enabled" : "disabled"; } static const char * diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index f2940b29595f..f8e247d9e5c9 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -64,16 +64,13 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int snd_seq_oss_midi_lookup_ports(int client) { - struct snd_seq_client_info *clinfo; - struct snd_seq_port_info *pinfo; + struct snd_seq_client_info *clinfo __free(kfree) = NULL; + struct snd_seq_port_info *pinfo __free(kfree) = NULL; clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL); pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); - if (! clinfo || ! pinfo) { - kfree(clinfo); - kfree(pinfo); + if (!clinfo || !pinfo) return -ENOMEM; - } clinfo->client = -1; while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) { if (clinfo->client == client) @@ -83,8 +80,6 @@ snd_seq_oss_midi_lookup_ports(int client) while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0) snd_seq_oss_midi_check_new_port(pinfo); } - kfree(clinfo); - kfree(pinfo); return 0; } diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 1e35bf086a51..643af4c1e838 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -31,8 +31,8 @@ struct snd_seq_port_info32 { static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned int cmd, struct snd_seq_port_info32 __user *data32) { - int err = -EFAULT; - struct snd_seq_port_info *data; + struct snd_seq_port_info *data __free(kfree) = NULL; + int err; data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -41,20 +41,18 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned if (copy_from_user(data, data32, sizeof(*data32)) || get_user(data->flags, &data32->flags) || get_user(data->time_queue, &data32->time_queue)) - goto error; + return -EFAULT; data->kernel = NULL; err = snd_seq_kernel_client_ctl(client->number, cmd, data); if (err < 0) - goto error; + return err; if (copy_to_user(data32, data, sizeof(*data32)) || put_user(data->flags, &data32->flags) || put_user(data->time_queue, &data32->time_queue)) - err = -EFAULT; + return -EFAULT; - error: - kfree(data); return err; } diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index f8e02e98709a..3a10b081f129 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -88,12 +88,11 @@ void snd_seq_fifo_clear(struct snd_seq_fifo *f) atomic_set(&f->overflow, 0); snd_use_lock_sync(&f->use_lock); - spin_lock_irq(&f->lock); + guard(spinlock_irq)(&f->lock); /* drain the fifo */ while ((cell = fifo_cell_out(f)) != NULL) { snd_seq_cell_free(cell); } - spin_unlock_irq(&f->lock); } @@ -102,7 +101,6 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event) { struct snd_seq_event_cell *cell; - unsigned long flags; int err; if (snd_BUG_ON(!f)) @@ -118,15 +116,15 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, } /* append new cells to fifo */ - spin_lock_irqsave(&f->lock, flags); - if (f->tail != NULL) - f->tail->next = cell; - f->tail = cell; - if (f->head == NULL) - f->head = cell; - cell->next = NULL; - f->cells++; - spin_unlock_irqrestore(&f->lock, flags); + scoped_guard(spinlock_irqsave, &f->lock) { + if (f->tail != NULL) + f->tail->next = cell; + f->tail = cell; + if (f->head == NULL) + f->head = cell; + cell->next = NULL; + f->cells++; + } /* wakeup client */ if (waitqueue_active(&f->input_sleep)) @@ -199,16 +197,13 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, struct snd_seq_event_cell *cell) { - unsigned long flags; - if (cell) { - spin_lock_irqsave(&f->lock, flags); + guard(spinlock_irqsave)(&f->lock); cell->next = f->head; f->head = cell; if (!f->tail) f->tail = cell; f->cells++; - spin_unlock_irqrestore(&f->lock, flags); } } @@ -239,17 +234,17 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) return -ENOMEM; } - spin_lock_irq(&f->lock); - /* remember old pool */ - oldpool = f->pool; - oldhead = f->head; - /* exchange pools */ - f->pool = newpool; - f->head = NULL; - f->tail = NULL; - f->cells = 0; - /* NOTE: overflow flag is not cleared */ - spin_unlock_irq(&f->lock); + scoped_guard(spinlock_irq, &f->lock) { + /* remember old pool */ + oldpool = f->pool; + oldhead = f->head; + /* exchange pools */ + f->pool = newpool; + f->head = NULL; + f->tail = NULL; + f->cells = 0; + /* NOTE: overflow flag is not cleared */ + } /* close the old pool and wait until all users are gone */ snd_seq_pool_mark_closing(oldpool); @@ -268,16 +263,14 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) /* get the number of unused cells safely */ int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f) { - unsigned long flags; int cells; if (!f) return 0; snd_use_lock_use(&f->use_lock); - spin_lock_irqsave(&f->lock, flags); - cells = snd_seq_unused_cells(f->pool); - spin_unlock_irqrestore(&f->lock, flags); + scoped_guard(spinlock_irqsave, &f->lock) + cells = snd_seq_unused_cells(f->pool); snd_use_lock_free(&f->use_lock); return cells; } diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index e705e7538118..20155e3e87c6 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -232,7 +232,6 @@ static inline void free_cell(struct snd_seq_pool *pool, void snd_seq_cell_free(struct snd_seq_event_cell * cell) { - unsigned long flags; struct snd_seq_pool *pool; if (snd_BUG_ON(!cell)) @@ -241,7 +240,7 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell) if (snd_BUG_ON(!pool)) return; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); free_cell(pool, cell); if (snd_seq_ev_is_variable(&cell->event)) { if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) { @@ -259,7 +258,6 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell) if (snd_seq_output_ok(pool)) wake_up(&pool->output_sleep); } - spin_unlock_irqrestore(&pool->lock, flags); } @@ -449,9 +447,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) return -ENOMEM; /* add new cells to the free cell list */ - spin_lock_irq(&pool->lock); + guard(spinlock_irq)(&pool->lock); if (pool->ptr) { - spin_unlock_irq(&pool->lock); kvfree(cellptr); return 0; } @@ -470,20 +467,16 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) /* init statistics */ pool->max_used = 0; pool->total_elements = pool->size; - spin_unlock_irq(&pool->lock); return 0; } /* refuse the further insertion to the pool */ void snd_seq_pool_mark_closing(struct snd_seq_pool *pool) { - unsigned long flags; - if (snd_BUG_ON(!pool)) return; - spin_lock_irqsave(&pool->lock, flags); + guard(spinlock_irqsave)(&pool->lock); pool->closing = 1; - spin_unlock_irqrestore(&pool->lock, flags); } /* remove events */ @@ -502,18 +495,17 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) schedule_timeout_uninterruptible(1); /* release all resources */ - spin_lock_irq(&pool->lock); - ptr = pool->ptr; - pool->ptr = NULL; - pool->free = NULL; - pool->total_elements = 0; - spin_unlock_irq(&pool->lock); + scoped_guard(spinlock_irq, &pool->lock) { + ptr = pool->ptr; + pool->ptr = NULL; + pool->free = NULL; + pool->total_elements = 0; + } kvfree(ptr); - spin_lock_irq(&pool->lock); + guard(spinlock_irq)(&pool->lock); pool->closing = 0; - spin_unlock_irq(&pool->lock); return 0; } diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 18320a248aa7..ba52a77eda38 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -113,6 +113,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i return 0; } +/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */ +static int __dump_midi(void *ptr, void *buf, int count) +{ + return dump_midi(ptr, buf, count); +} + static int event_process_midi(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop) { @@ -132,7 +138,7 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); return 0; } - snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_seq_dump_var_event(ev, __dump_midi, substream); snd_midi_event_reset_decode(msynth->parser); } else { if (msynth->parser == NULL) @@ -264,8 +270,8 @@ snd_seq_midisynth_probe(struct device *_dev) struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; - struct snd_seq_port_info *port; - struct snd_rawmidi_info *info; + struct snd_seq_port_info *port __free(kfree) = NULL; + struct snd_rawmidi_info *info __free(kfree) = NULL; struct snd_rawmidi *rmidi = dev->private_data; int newclient = 0; unsigned int p, ports; @@ -291,31 +297,24 @@ snd_seq_midisynth_probe(struct device *_dev) ports = output_count; if (ports < input_count) ports = input_count; - if (ports == 0) { - kfree(info); + if (ports == 0) return -ENODEV; - } if (ports > (256 / SNDRV_RAWMIDI_DEVICES)) ports = 256 / SNDRV_RAWMIDI_DEVICES; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); client = synths[card->number]; if (client == NULL) { newclient = 1; client = kzalloc(sizeof(*client), GFP_KERNEL); - if (client == NULL) { - mutex_unlock(®ister_mutex); - kfree(info); + if (client == NULL) return -ENOMEM; - } client->seq_client = snd_seq_create_kernel_client( card, 0, "%s", card->shortname[0] ? (const char *)card->shortname : "External MIDI"); if (client->seq_client < 0) { kfree(client); - mutex_unlock(®ister_mutex); - kfree(info); return -ENOMEM; } } @@ -396,9 +395,6 @@ snd_seq_midisynth_probe(struct device *_dev) client->num_ports++; if (newclient) synths[card->number] = client; - mutex_unlock(®ister_mutex); - kfree(info); - kfree(port); return 0; /* success */ __nomem: @@ -411,9 +407,6 @@ snd_seq_midisynth_probe(struct device *_dev) snd_seq_delete_kernel_client(client->seq_client); kfree(client); } - kfree(info); - kfree(port); - mutex_unlock(®ister_mutex); return -ENOMEM; } @@ -427,12 +420,10 @@ snd_seq_midisynth_remove(struct device *_dev) struct snd_card *card = dev->card; int device = dev->device, p, ports; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); client = synths[card->number]; - if (client == NULL || client->ports[device] == NULL) { - mutex_unlock(®ister_mutex); + if (client == NULL || client->ports[device] == NULL) return -ENODEV; - } ports = client->ports_per_device[device]; client->ports_per_device[device] = 0; msynth = client->ports[device]; @@ -446,7 +437,6 @@ snd_seq_midisynth_remove(struct device *_dev) synths[card->number] = NULL; kfree(client); } - mutex_unlock(®ister_mutex); return 0; } diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index 7511462fe071..fa9dfc53c3fc 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -144,21 +144,15 @@ static inline void reset_encode(struct snd_midi_event *dev) void snd_midi_event_reset_encode(struct snd_midi_event *dev) { - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); + guard(spinlock_irqsave)(&dev->lock); reset_encode(dev); - spin_unlock_irqrestore(&dev->lock, flags); } EXPORT_SYMBOL(snd_midi_event_reset_encode); void snd_midi_event_reset_decode(struct snd_midi_event *dev) { - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); + guard(spinlock_irqsave)(&dev->lock); dev->lastcmd = 0xff; - spin_unlock_irqrestore(&dev->lock, flags); } EXPORT_SYMBOL(snd_midi_event_reset_decode); @@ -177,7 +171,6 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c, struct snd_seq_event *ev) { bool rc = false; - unsigned long flags; if (c >= MIDI_CMD_COMMON_CLOCK) { /* real-time event */ @@ -187,7 +180,7 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c, return ev->type != SNDRV_SEQ_EVENT_NONE; } - spin_lock_irqsave(&dev->lock, flags); + guard(spinlock_irqsave)(&dev->lock); if ((c & 0x80) && (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) { /* new command */ @@ -236,7 +229,6 @@ bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c, } } - spin_unlock_irqrestore(&dev->lock, flags); return rc; } EXPORT_SYMBOL(snd_midi_event_encode_byte); diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index f3f14ff0f80f..ca631ca4f2c6 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -48,17 +48,15 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client, if (client == NULL) return NULL; - read_lock(&client->ports_lock); + guard(read_lock)(&client->ports_lock); list_for_each_entry(port, &client->ports_list_head, list) { if (port->addr.port == num) { if (port->closing) break; /* deleting now */ snd_use_lock_use(&port->use_lock); - read_unlock(&client->ports_lock); return port; } } - read_unlock(&client->ports_lock); return NULL; /* not found */ } @@ -73,7 +71,7 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl num = pinfo->addr.port; found = NULL; - read_lock(&client->ports_lock); + guard(read_lock)(&client->ports_lock); list_for_each_entry(port, &client->ports_list_head, list) { if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) && !check_inactive) @@ -93,7 +91,6 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl else snd_use_lock_use(&found->use_lock); } - read_unlock(&client->ports_lock); return found; } @@ -145,13 +142,12 @@ int snd_seq_create_port(struct snd_seq_client *client, int port, snd_use_lock_use(&new_port->use_lock); num = max(port, 0); - mutex_lock(&client->ports_mutex); - write_lock_irq(&client->ports_lock); + guard(mutex)(&client->ports_mutex); + guard(write_lock_irq)(&client->ports_lock); list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) { kfree(new_port); - num = -EBUSY; - goto unlock; + return -EBUSY; } if (p->addr.port > num) break; @@ -164,9 +160,6 @@ int snd_seq_create_port(struct snd_seq_client *client, int port, new_port->addr.port = num; /* store the port number in the port */ sprintf(new_port->name, "port-%d", num); *port_ret = new_port; - unlock: - write_unlock_irq(&client->ports_lock); - mutex_unlock(&client->ports_mutex); return num; } @@ -281,19 +274,18 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) { struct snd_seq_client_port *found = NULL, *p; - mutex_lock(&client->ports_mutex); - write_lock_irq(&client->ports_lock); - list_for_each_entry(p, &client->ports_list_head, list) { - if (p->addr.port == port) { - /* ok found. delete from the list at first */ - list_del(&p->list); - client->num_ports--; - found = p; - break; + scoped_guard(mutex, &client->ports_mutex) { + guard(write_lock_irq)(&client->ports_lock); + list_for_each_entry(p, &client->ports_list_head, list) { + if (p->addr.port == port) { + /* ok found. delete from the list at first */ + list_del(&p->list); + client->num_ports--; + found = p; + break; + } } } - write_unlock_irq(&client->ports_lock); - mutex_unlock(&client->ports_mutex); if (found) return port_delete(client, found); else @@ -309,16 +301,16 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client) /* move the port list to deleted_list, and * clear the port list in the client data. */ - mutex_lock(&client->ports_mutex); - write_lock_irq(&client->ports_lock); - if (! list_empty(&client->ports_list_head)) { - list_add(&deleted_list, &client->ports_list_head); - list_del_init(&client->ports_list_head); - } else { - INIT_LIST_HEAD(&deleted_list); + guard(mutex)(&client->ports_mutex); + scoped_guard(write_lock_irq, &client->ports_lock) { + if (!list_empty(&client->ports_list_head)) { + list_add(&deleted_list, &client->ports_list_head); + list_del_init(&client->ports_list_head); + } else { + INIT_LIST_HEAD(&deleted_list); + } + client->num_ports = 0; } - client->num_ports = 0; - write_unlock_irq(&client->ports_lock); /* remove each port in deleted_list */ list_for_each_entry_safe(port, tmp, &deleted_list, list) { @@ -326,7 +318,6 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client) snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } - mutex_unlock(&client->ports_mutex); return 0; } @@ -506,42 +497,37 @@ static int check_and_subscribe_port(struct snd_seq_client *client, int err; grp = is_src ? &port->c_src : &port->c_dest; - err = -EBUSY; - down_write(&grp->list_mutex); + guard(rwsem_write)(&grp->list_mutex); if (exclusive) { if (!list_empty(&grp->list_head)) - goto __error; + return -EBUSY; } else { if (grp->exclusive) - goto __error; + return -EBUSY; /* check whether already exists */ list_for_each(p, &grp->list_head) { s = get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) - goto __error; + return -EBUSY; } } err = subscribe_port(client, port, grp, &subs->info, ack); if (err < 0) { grp->exclusive = 0; - goto __error; + return err; } /* add to list */ - write_lock_irq(&grp->list_lock); + guard(write_lock_irq)(&grp->list_lock); if (is_src) list_add_tail(&subs->src_list, &grp->list_head); else list_add_tail(&subs->dest_list, &grp->list_head); grp->exclusive = exclusive; atomic_inc(&subs->ref_count); - write_unlock_irq(&grp->list_lock); - err = 0; - __error: - up_write(&grp->list_mutex); - return err; + return 0; } /* called with grp->list_mutex held */ @@ -556,12 +542,12 @@ static void __delete_and_unsubscribe_port(struct snd_seq_client *client, grp = is_src ? &port->c_src : &port->c_dest; list = is_src ? &subs->src_list : &subs->dest_list; - write_lock_irq(&grp->list_lock); - empty = list_empty(list); - if (!empty) - list_del_init(list); - grp->exclusive = 0; - write_unlock_irq(&grp->list_lock); + scoped_guard(write_lock_irq, &grp->list_lock) { + empty = list_empty(list); + if (!empty) + list_del_init(list); + grp->exclusive = 0; + } if (!empty) unsubscribe_port(client, port, grp, &subs->info, ack); @@ -575,9 +561,8 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, struct snd_seq_port_subs_info *grp; grp = is_src ? &port->c_src : &port->c_dest; - down_write(&grp->list_mutex); + guard(rwsem_write)(&grp->list_mutex); __delete_and_unsubscribe_port(client, port, subs, is_src, ack); - up_write(&grp->list_mutex); } /* connect two ports */ @@ -639,18 +624,18 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, /* always start from deleting the dest port for avoiding concurrent * deletions */ - down_write(&dest->list_mutex); - /* look for the connection */ - list_for_each_entry(subs, &dest->list_head, dest_list) { - if (match_subs_info(info, &subs->info)) { - __delete_and_unsubscribe_port(dest_client, dest_port, - subs, false, - connector->number != dest_client->number); - err = 0; - break; + scoped_guard(rwsem_write, &dest->list_mutex) { + /* look for the connection */ + list_for_each_entry(subs, &dest->list_head, dest_list) { + if (match_subs_info(info, &subs->info)) { + __delete_and_unsubscribe_port(dest_client, dest_port, + subs, false, + connector->number != dest_client->number); + err = 0; + break; + } } } - up_write(&dest->list_mutex); if (err < 0) return err; @@ -669,7 +654,7 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp, struct snd_seq_subscribers *s; int err = -ENOENT; - down_read(&src_grp->list_mutex); + guard(rwsem_read)(&src_grp->list_mutex); list_for_each_entry(s, &src_grp->list_head, src_list) { if (addr_match(dest_addr, &s->info.dest)) { *subs = s->info; @@ -677,7 +662,6 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp, break; } } - up_read(&src_grp->list_mutex); return err; } diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 1d857981e876..e649485a8772 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -132,7 +132,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, struct snd_seq_event_cell * cell) { struct snd_seq_event_cell *cur, *prev; - unsigned long flags; int count; int prior; @@ -142,7 +141,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, /* check flags */ prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); - spin_lock_irqsave(&f->lock, flags); + guard(spinlock_irqsave)(&f->lock); /* check if this element needs to inserted at the end (ie. ordered data is inserted) This will be very likeley if a sequencer @@ -154,7 +153,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, f->tail = cell; cell->next = NULL; f->cells++; - spin_unlock_irqrestore(&f->lock, flags); return 0; } } @@ -179,7 +177,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, prev = cur; cur = cur->next; if (! --count) { - spin_unlock_irqrestore(&f->lock, flags); pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n"); return -EINVAL; } @@ -195,7 +192,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, if (cur == NULL) /* reached end of the list */ f->tail = cell; f->cells++; - spin_unlock_irqrestore(&f->lock, flags); return 0; } @@ -213,14 +209,13 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f, void *current_time) { struct snd_seq_event_cell *cell; - unsigned long flags; if (f == NULL) { pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n"); return NULL; } - spin_lock_irqsave(&f->lock, flags); + guard(spinlock_irqsave)(&f->lock); cell = f->head; if (cell && current_time && !event_is_ready(&cell->event, current_time)) cell = NULL; @@ -235,7 +230,6 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f, f->cells--; } - spin_unlock_irqrestore(&f->lock, flags); return cell; } @@ -249,73 +243,43 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f) return f->cells; } -static inline int prioq_match(struct snd_seq_event_cell *cell, - int client, int timestamp) -{ - if (cell->event.source.client == client || - cell->event.dest.client == client) - return 1; - if (!timestamp) - return 0; - switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { - case SNDRV_SEQ_TIME_STAMP_TICK: - if (cell->event.time.tick) - return 1; - break; - case SNDRV_SEQ_TIME_STAMP_REAL: - if (cell->event.time.time.tv_sec || - cell->event.time.time.tv_nsec) - return 1; - break; - } - return 0; -} - -/* remove cells for left client */ -void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp) +/* remove cells matching with the condition */ +static void prioq_remove_cells(struct snd_seq_prioq *f, + bool (*match)(struct snd_seq_event_cell *cell, + void *arg), + void *arg) { register struct snd_seq_event_cell *cell, *next; - unsigned long flags; struct snd_seq_event_cell *prev = NULL; struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext; /* collect all removed cells */ - spin_lock_irqsave(&f->lock, flags); - cell = f->head; - while (cell) { - next = cell->next; - if (prioq_match(cell, client, timestamp)) { + scoped_guard(spinlock_irqsave, &f->lock) { + for (cell = f->head; cell; cell = next) { + next = cell->next; + if (!match(cell, arg)) { + prev = cell; + continue; + } + /* remove cell from prioq */ - if (cell == f->head) { + if (cell == f->head) f->head = cell->next; - } else { + else prev->next = cell->next; - } if (cell == f->tail) f->tail = cell->next; f->cells--; + /* add cell to free list */ cell->next = NULL; - if (freefirst == NULL) { + if (freefirst == NULL) freefirst = cell; - } else { + else freeprev->next = cell; - } freeprev = cell; - } else { -#if 0 - pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, " - "client = %i\n", - cell->event.type, - cell->event.source.client, - cell->event.dest.client, - client); -#endif - prev = cell; } - cell = next; } - spin_unlock_irqrestore(&f->lock, flags); /* remove selected cells */ while (freefirst) { @@ -325,22 +289,68 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp) } } -static int prioq_remove_match(struct snd_seq_remove_events *info, - struct snd_seq_event *ev) +struct prioq_match_arg { + int client; + int timestamp; +}; + +static inline bool prioq_match(struct snd_seq_event_cell *cell, void *arg) +{ + struct prioq_match_arg *v = arg; + + if (cell->event.source.client == v->client || + cell->event.dest.client == v->client) + return true; + if (!v->timestamp) + return false; + switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) { + case SNDRV_SEQ_TIME_STAMP_TICK: + if (cell->event.time.tick) + return true; + break; + case SNDRV_SEQ_TIME_STAMP_REAL: + if (cell->event.time.time.tv_sec || + cell->event.time.time.tv_nsec) + return true; + break; + } + return false; +} + +/* remove cells for left client */ +void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp) { + struct prioq_match_arg arg = { client, timestamp }; + + return prioq_remove_cells(f, prioq_match, &arg); +} + +struct prioq_remove_match_arg { + int client; + struct snd_seq_remove_events *info; +}; + +static bool prioq_remove_match(struct snd_seq_event_cell *cell, void *arg) +{ + struct prioq_remove_match_arg *v = arg; + struct snd_seq_event *ev = &cell->event; + struct snd_seq_remove_events *info = v->info; int res; + if (ev->source.client != v->client) + return false; + if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) { if (ev->dest.client != info->dest.client || ev->dest.port != info->dest.port) - return 0; + return false; } if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) { if (! snd_seq_ev_is_channel_type(ev)) - return 0; + return false; /* data.note.channel and data.control.channel are identical */ if (ev->data.note.channel != info->channel) - return 0; + return false; } if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) { if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) @@ -348,7 +358,7 @@ static int prioq_remove_match(struct snd_seq_remove_events *info, else res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); if (!res) - return 0; + return false; } if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) { if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK) @@ -356,81 +366,35 @@ static int prioq_remove_match(struct snd_seq_remove_events *info, else res = snd_seq_compare_real_time(&ev->time.time, &info->time.time); if (res) - return 0; + return false; } if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) { if (ev->type != info->type) - return 0; + return false; } if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) { /* Do not remove off events */ switch (ev->type) { case SNDRV_SEQ_EVENT_NOTEOFF: /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */ - return 0; + return false; default: break; } } if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) { if (info->tag != ev->tag) - return 0; + return false; } - return 1; + return true; } /* remove cells matching remove criteria */ void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client, struct snd_seq_remove_events *info) { - struct snd_seq_event_cell *cell, *next; - unsigned long flags; - struct snd_seq_event_cell *prev = NULL; - struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext; - - /* collect all removed cells */ - spin_lock_irqsave(&f->lock, flags); - cell = f->head; - - while (cell) { - next = cell->next; - if (cell->event.source.client == client && - prioq_remove_match(info, &cell->event)) { + struct prioq_remove_match_arg arg = { client, info }; - /* remove cell from prioq */ - if (cell == f->head) { - f->head = cell->next; - } else { - prev->next = cell->next; - } - - if (cell == f->tail) - f->tail = cell->next; - f->cells--; - - /* add cell to free list */ - cell->next = NULL; - if (freefirst == NULL) { - freefirst = cell; - } else { - freeprev->next = cell; - } - - freeprev = cell; - } else { - prev = cell; - } - cell = next; - } - spin_unlock_irqrestore(&f->lock, flags); - - /* remove selected cells */ - while (freefirst) { - freenext = freefirst->next; - snd_seq_cell_free(freefirst); - freefirst = freenext; - } + return prioq_remove_cells(f, prioq_remove_match, &arg); } - - diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index bc933104c3ee..500ee6b19c71 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -50,43 +50,35 @@ int snd_seq_queue_get_cur_queues(void) static int queue_list_add(struct snd_seq_queue *q) { int i; - unsigned long flags; - spin_lock_irqsave(&queue_list_lock, flags); + guard(spinlock_irqsave)(&queue_list_lock); for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { if (! queue_list[i]) { queue_list[i] = q; q->queue = i; num_queues++; - spin_unlock_irqrestore(&queue_list_lock, flags); return i; } } - spin_unlock_irqrestore(&queue_list_lock, flags); return -1; } static struct snd_seq_queue *queue_list_remove(int id, int client) { struct snd_seq_queue *q; - unsigned long flags; - spin_lock_irqsave(&queue_list_lock, flags); + guard(spinlock_irqsave)(&queue_list_lock); q = queue_list[id]; if (q) { - spin_lock(&q->owner_lock); + guard(spinlock)(&q->owner_lock); if (q->owner == client) { /* found */ q->klocked = 1; - spin_unlock(&q->owner_lock); queue_list[id] = NULL; num_queues--; - spin_unlock_irqrestore(&queue_list_lock, flags); return q; } - spin_unlock(&q->owner_lock); } - spin_unlock_irqrestore(&queue_list_lock, flags); return NULL; } @@ -203,15 +195,13 @@ int snd_seq_queue_delete(int client, int queueid) struct snd_seq_queue *queueptr(int queueid) { struct snd_seq_queue *q; - unsigned long flags; if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES) return NULL; - spin_lock_irqsave(&queue_list_lock, flags); + guard(spinlock_irqsave)(&queue_list_lock); q = queue_list[queueid]; if (q) snd_use_lock_use(&q->use_lock); - spin_unlock_irqrestore(&queue_list_lock, flags); return q; } @@ -239,7 +229,6 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name) void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) { - unsigned long flags; struct snd_seq_event_cell *cell; snd_seq_tick_time_t cur_tick; snd_seq_real_time_t cur_time; @@ -249,14 +238,13 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) return; /* make this function non-reentrant */ - spin_lock_irqsave(&q->check_lock, flags); - if (q->check_blocked) { - q->check_again = 1; - spin_unlock_irqrestore(&q->check_lock, flags); - return; /* other thread is already checking queues */ + scoped_guard(spinlock_irqsave, &q->check_lock) { + if (q->check_blocked) { + q->check_again = 1; + return; /* other thread is already checking queues */ + } + q->check_blocked = 1; } - q->check_blocked = 1; - spin_unlock_irqrestore(&q->check_lock, flags); __again: /* Process tick queue... */ @@ -283,16 +271,14 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) out: /* free lock */ - spin_lock_irqsave(&q->check_lock, flags); - if (q->check_again) { - q->check_again = 0; - if (processed < MAX_CELL_PROCESSES_IN_QUEUE) { - spin_unlock_irqrestore(&q->check_lock, flags); - goto __again; + scoped_guard(spinlock_irqsave, &q->check_lock) { + if (q->check_again) { + q->check_again = 0; + if (processed < MAX_CELL_PROCESSES_IN_QUEUE) + goto __again; } + q->check_blocked = 0; } - q->check_blocked = 0; - spin_unlock_irqrestore(&q->check_lock, flags); } @@ -361,25 +347,20 @@ static inline int check_access(struct snd_seq_queue *q, int client) */ static int queue_access_lock(struct snd_seq_queue *q, int client) { - unsigned long flags; int access_ok; - spin_lock_irqsave(&q->owner_lock, flags); + guard(spinlock_irqsave)(&q->owner_lock); access_ok = check_access(q, client); if (access_ok) q->klocked = 1; - spin_unlock_irqrestore(&q->owner_lock, flags); return access_ok; } /* unlock the queue */ static inline void queue_access_unlock(struct snd_seq_queue *q) { - unsigned long flags; - - spin_lock_irqsave(&q->owner_lock, flags); + guard(spinlock_irqsave)(&q->owner_lock); q->klocked = 0; - spin_unlock_irqrestore(&q->owner_lock, flags); } /* exported - only checking permission */ @@ -387,13 +368,11 @@ int snd_seq_queue_check_access(int queueid, int client) { struct snd_seq_queue *q = queueptr(queueid); int access_ok; - unsigned long flags; if (! q) return 0; - spin_lock_irqsave(&q->owner_lock, flags); - access_ok = check_access(q, client); - spin_unlock_irqrestore(&q->owner_lock, flags); + scoped_guard(spinlock_irqsave, &q->owner_lock) + access_ok = check_access(q, client); queuefree(q); return access_ok; } @@ -406,7 +385,6 @@ int snd_seq_queue_check_access(int queueid, int client) int snd_seq_queue_set_owner(int queueid, int client, int locked) { struct snd_seq_queue *q = queueptr(queueid); - unsigned long flags; if (q == NULL) return -EINVAL; @@ -416,10 +394,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked) return -EPERM; } - spin_lock_irqsave(&q->owner_lock, flags); - q->locked = locked ? 1 : 0; - q->owner = client; - spin_unlock_irqrestore(&q->owner_lock, flags); + scoped_guard(spinlock_irqsave, &q->owner_lock) { + q->locked = locked ? 1 : 0; + q->owner = client; + } queue_access_unlock(q); queuefree(q); @@ -750,10 +728,10 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, else bpm = 0; - spin_lock_irq(&q->owner_lock); - locked = q->locked; - owner = q->owner; - spin_unlock_irq(&q->owner_lock); + scoped_guard(spinlock_irq, &q->owner_lock) { + locked = q->locked; + owner = q->owner; + } snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name); snd_iprintf(buffer, "owned by client : %d\n", owner); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 9863be6fd43e..ad2b97e2762d 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -75,9 +75,7 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr) void snd_seq_timer_defaults(struct snd_seq_timer * tmr) { - unsigned long flags; - - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); /* setup defaults */ tmr->ppq = 96; /* 96 PPQ */ tmr->tempo = 500000; /* 120 BPM */ @@ -93,7 +91,6 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) tmr->preferred_resolution = seq_default_timer_resolution; tmr->skew = tmr->skew_base = SKEW_BASE; - spin_unlock_irqrestore(&tmr->lock, flags); } static void seq_timer_reset(struct snd_seq_timer *tmr) @@ -108,11 +105,8 @@ static void seq_timer_reset(struct snd_seq_timer *tmr) void snd_seq_timer_reset(struct snd_seq_timer *tmr) { - unsigned long flags; - - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); seq_timer_reset(tmr); - spin_unlock_irqrestore(&tmr->lock, flags); } @@ -121,7 +115,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, unsigned long resolution, unsigned long ticks) { - unsigned long flags; struct snd_seq_queue *q = timeri->callback_data; struct snd_seq_timer *tmr; @@ -130,29 +123,27 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, tmr = q->timer; if (tmr == NULL) return; - spin_lock_irqsave(&tmr->lock, flags); - if (!tmr->running) { - spin_unlock_irqrestore(&tmr->lock, flags); - return; - } - resolution *= ticks; - if (tmr->skew != tmr->skew_base) { - /* FIXME: assuming skew_base = 0x10000 */ - resolution = (resolution >> 16) * tmr->skew + - (((resolution & 0xffff) * tmr->skew) >> 16); - } + scoped_guard(spinlock_irqsave, &tmr->lock) { + if (!tmr->running) + return; - /* update timer */ - snd_seq_inc_time_nsec(&tmr->cur_time, resolution); + resolution *= ticks; + if (tmr->skew != tmr->skew_base) { + /* FIXME: assuming skew_base = 0x10000 */ + resolution = (resolution >> 16) * tmr->skew + + (((resolution & 0xffff) * tmr->skew) >> 16); + } - /* calculate current tick */ - snd_seq_timer_update_tick(&tmr->tick, resolution); + /* update timer */ + snd_seq_inc_time_nsec(&tmr->cur_time, resolution); - /* register actual time of this timer update */ - ktime_get_ts64(&tmr->last_update); + /* calculate current tick */ + snd_seq_timer_update_tick(&tmr->tick, resolution); - spin_unlock_irqrestore(&tmr->lock, flags); + /* register actual time of this timer update */ + ktime_get_ts64(&tmr->last_update); + } /* check queues and dispatch events */ snd_seq_check_queue(q, 1, 0); @@ -161,18 +152,15 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, /* set current tempo */ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) { - unsigned long flags; - if (snd_BUG_ON(!tmr)) return -EINVAL; if (tempo <= 0) return -EINVAL; - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); if ((unsigned int)tempo != tmr->tempo) { tmr->tempo = tempo; snd_seq_timer_set_tick_resolution(tmr); } - spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -180,17 +168,15 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) { int changed; - unsigned long flags; if (snd_BUG_ON(!tmr)) return -EINVAL; if (tempo <= 0 || ppq <= 0) return -EINVAL; - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); if (tmr->running && (ppq != tmr->ppq)) { /* refuse to change ppq on running timers */ /* because it will upset the song position (ticks) */ - spin_unlock_irqrestore(&tmr->lock, flags); pr_debug("ALSA: seq: cannot change ppq of a running timer\n"); return -EBUSY; } @@ -199,7 +185,6 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) tmr->ppq = ppq; if (changed) snd_seq_timer_set_tick_resolution(tmr); - spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -207,15 +192,12 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq) int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position) { - unsigned long flags; - if (snd_BUG_ON(!tmr)) return -EINVAL; - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); tmr->tick.cur_tick = position; tmr->tick.fraction = 0; - spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -223,15 +205,12 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position) { - unsigned long flags; - if (snd_BUG_ON(!tmr)) return -EINVAL; snd_seq_sanity_real_time(&position); - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); tmr->cur_time = position; - spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -239,8 +218,6 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base) { - unsigned long flags; - if (snd_BUG_ON(!tmr)) return -EINVAL; @@ -249,9 +226,8 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, pr_debug("ALSA: seq: invalid skew base 0x%x\n", base); return -EINVAL; } - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); tmr->skew = skew; - spin_unlock_irqrestore(&tmr->lock, flags); return 0; } @@ -296,12 +272,12 @@ int snd_seq_timer_open(struct snd_seq_queue *q) snd_timer_instance_free(t); return err; } - spin_lock_irq(&tmr->lock); - if (tmr->timeri) - err = -EBUSY; - else - tmr->timeri = t; - spin_unlock_irq(&tmr->lock); + scoped_guard(spinlock_irq, &tmr->lock) { + if (tmr->timeri) + err = -EBUSY; + else + tmr->timeri = t; + } if (err < 0) { snd_timer_close(t); snd_timer_instance_free(t); @@ -318,10 +294,10 @@ int snd_seq_timer_close(struct snd_seq_queue *q) tmr = q->timer; if (snd_BUG_ON(!tmr)) return -EINVAL; - spin_lock_irq(&tmr->lock); - t = tmr->timeri; - tmr->timeri = NULL; - spin_unlock_irq(&tmr->lock); + scoped_guard(spinlock_irq, &tmr->lock) { + t = tmr->timeri; + tmr->timeri = NULL; + } if (t) { snd_timer_close(t); snd_timer_instance_free(t); @@ -342,13 +318,8 @@ static int seq_timer_stop(struct snd_seq_timer *tmr) int snd_seq_timer_stop(struct snd_seq_timer *tmr) { - unsigned long flags; - int err; - - spin_lock_irqsave(&tmr->lock, flags); - err = seq_timer_stop(tmr); - spin_unlock_irqrestore(&tmr->lock, flags); - return err; + guard(spinlock_irqsave)(&tmr->lock); + return seq_timer_stop(tmr); } static int initialize_timer(struct snd_seq_timer *tmr) @@ -398,13 +369,8 @@ static int seq_timer_start(struct snd_seq_timer *tmr) int snd_seq_timer_start(struct snd_seq_timer *tmr) { - unsigned long flags; - int err; - - spin_lock_irqsave(&tmr->lock, flags); - err = seq_timer_start(tmr); - spin_unlock_irqrestore(&tmr->lock, flags); - return err; + guard(spinlock_irqsave)(&tmr->lock); + return seq_timer_start(tmr); } static int seq_timer_continue(struct snd_seq_timer *tmr) @@ -426,13 +392,8 @@ static int seq_timer_continue(struct snd_seq_timer *tmr) int snd_seq_timer_continue(struct snd_seq_timer *tmr) { - unsigned long flags; - int err; - - spin_lock_irqsave(&tmr->lock, flags); - err = seq_timer_continue(tmr); - spin_unlock_irqrestore(&tmr->lock, flags); - return err; + guard(spinlock_irqsave)(&tmr->lock); + return seq_timer_continue(tmr); } /* return current 'real' time. use timeofday() to get better granularity. */ @@ -440,9 +401,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr, bool adjust_ktime) { snd_seq_real_time_t cur_time; - unsigned long flags; - spin_lock_irqsave(&tmr->lock, flags); + guard(spinlock_irqsave)(&tmr->lock); cur_time = tmr->cur_time; if (adjust_ktime && tmr->running) { struct timespec64 tm; @@ -453,7 +413,6 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr, cur_time.tv_sec += tm.tv_sec; snd_seq_sanity_real_time(&cur_time); } - spin_unlock_irqrestore(&tmr->lock, flags); return cur_time; } @@ -461,13 +420,8 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr, high PPQ values) */ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr) { - snd_seq_tick_time_t cur_tick; - unsigned long flags; - - spin_lock_irqsave(&tmr->lock, flags); - cur_tick = tmr->tick.cur_tick; - spin_unlock_irqrestore(&tmr->lock, flags); - return cur_tick; + guard(spinlock_irqsave)(&tmr->lock); + return tmr->tick.cur_tick; } @@ -486,19 +440,18 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, q = queueptr(idx); if (q == NULL) continue; - mutex_lock(&q->timer_mutex); - tmr = q->timer; - if (!tmr) - goto unlock; - ti = tmr->timeri; - if (!ti) - goto unlock; - snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); - resolution = snd_timer_resolution(ti) * tmr->ticks; - snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); - snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); -unlock: - mutex_unlock(&q->timer_mutex); + scoped_guard(mutex, &q->timer_mutex) { + tmr = q->timer; + if (!tmr) + break; + ti = tmr->timeri; + if (!ti) + break; + snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); + resolution = snd_timer_resolution(ti) * tmr->ticks; + snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); + snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); + } queuefree(q); } } diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 2db371d79930..c627d72f7fe2 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -115,21 +115,19 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, static int seq_ump_client_open(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - int err = 0; + int err; - mutex_lock(&ump->open_mutex); + guard(mutex)(&ump->open_mutex); if (dir == STR_OUT && !client->opened[dir]) { err = snd_rawmidi_kernel_open(&ump->core, 0, SNDRV_RAWMIDI_LFLG_OUTPUT | SNDRV_RAWMIDI_LFLG_APPEND, &client->out_rfile); if (err < 0) - goto unlock; + return err; } client->opened[dir]++; - unlock: - mutex_unlock(&ump->open_mutex); - return err; + return 0; } /* close the rawmidi */ @@ -137,11 +135,10 @@ static int seq_ump_client_close(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - mutex_lock(&ump->open_mutex); + guard(mutex)(&ump->open_mutex); if (!--client->opened[dir]) if (dir == STR_OUT) snd_rawmidi_kernel_release(&client->out_rfile); - mutex_unlock(&ump->open_mutex); return 0; } @@ -217,15 +214,12 @@ static void fill_port_info(struct snd_seq_port_info *port, static int seq_ump_group_init(struct seq_ump_client *client, int group_index) { struct seq_ump_group *group = &client->groups[group_index]; - struct snd_seq_port_info *port; + struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback pcallbacks; - int err; port = kzalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto error; - } + if (!port) + return -ENOMEM; fill_port_info(port, client, group); port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; @@ -238,24 +232,22 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index) pcallbacks.unuse = seq_ump_unuse; pcallbacks.event_input = seq_ump_process_event; port->kernel = &pcallbacks; - err = snd_seq_kernel_client_ctl(client->seq_client, - SNDRV_SEQ_IOCTL_CREATE_PORT, - port); - error: - kfree(port); - return err; + return snd_seq_kernel_client_ctl(client->seq_client, + SNDRV_SEQ_IOCTL_CREATE_PORT, + port); } /* update the sequencer ports; called from notify_fb_change callback */ static void update_port_infos(struct seq_ump_client *client) { - struct snd_seq_port_info *old, *new; + struct snd_seq_port_info *old __free(kfree) = NULL; + struct snd_seq_port_info *new __free(kfree) = NULL; int i, err; old = kzalloc(sizeof(*old), GFP_KERNEL); new = kzalloc(sizeof(*new), GFP_KERNEL); if (!old || !new) - goto error; + return; for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { old->addr.client = client->seq_client; @@ -264,7 +256,7 @@ static void update_port_infos(struct seq_ump_client *client) SNDRV_SEQ_IOCTL_GET_PORT_INFO, old); if (err < 0) - goto error; + return; fill_port_info(new, client, &client->groups[i]); if (old->capability == new->capability && !strcmp(old->name, new->name)) @@ -273,13 +265,10 @@ static void update_port_infos(struct seq_ump_client *client) SNDRV_SEQ_IOCTL_SET_PORT_INFO, new); if (err < 0) - goto error; + return; /* notify to system port */ snd_seq_system_client_ev_port_change(client->seq_client, i); } - error: - kfree(new); - kfree(old); } /* update dir_bits and active flag for all groups in the client */ @@ -334,7 +323,7 @@ static void update_group_attrs(struct seq_ump_client *client) /* create a UMP Endpoint port */ static int create_ump_endpoint_port(struct seq_ump_client *client) { - struct snd_seq_port_info *port; + struct snd_seq_port_info *port __free(kfree) = NULL; struct snd_seq_port_callback pcallbacks; unsigned int rawmidi_info = client->ump->core.info_flags; int err; @@ -383,7 +372,6 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) err = snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port); - kfree(port); return err; } diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 1b9260108e48..b4672613c261 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -62,6 +62,13 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi, /* * decode input event and put to read buffer of each opened file */ + +/* callback for snd_seq_dump_var_event(), bridging to snd_rawmidi_receive() */ +static int dump_to_rawmidi(void *ptr, void *buf, int count) +{ + return snd_rawmidi_receive(ptr, buf, count); +} + static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, struct snd_seq_event *ev, bool atomic) @@ -80,7 +87,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) continue; - snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream); + snd_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream); snd_midi_event_reset_decode(vmidi->parser); } else { len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev); @@ -192,11 +199,10 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) vmidi->client = rdev->client; vmidi->port = rdev->port; runtime->private_data = vmidi; - down_write(&rdev->filelist_sem); - write_lock_irq(&rdev->filelist_lock); - list_add_tail(&vmidi->list, &rdev->filelist); - write_unlock_irq(&rdev->filelist_lock); - up_write(&rdev->filelist_sem); + scoped_guard(rwsem_write, &rdev->filelist_sem) { + guard(write_lock_irq)(&rdev->filelist_lock); + list_add_tail(&vmidi->list, &rdev->filelist); + } vmidi->rdev = rdev; return 0; } @@ -236,11 +242,10 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream) struct snd_virmidi_dev *rdev = substream->rmidi->private_data; struct snd_virmidi *vmidi = substream->runtime->private_data; - down_write(&rdev->filelist_sem); - write_lock_irq(&rdev->filelist_lock); - list_del(&vmidi->list); - write_unlock_irq(&rdev->filelist_lock); - up_write(&rdev->filelist_sem); + scoped_guard(rwsem_write, &rdev->filelist_sem) { + guard(write_lock_irq)(&rdev->filelist_lock); + list_del(&vmidi->list); + } snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; kfree(vmidi); @@ -356,26 +361,22 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev) { int client; struct snd_seq_port_callback pcallbacks; - struct snd_seq_port_info *pinfo; + struct snd_seq_port_info *pinfo __free(kfree) = NULL; int err; if (rdev->client >= 0) return 0; pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); - if (!pinfo) { - err = -ENOMEM; - goto __error; - } + if (!pinfo) + return -ENOMEM; client = snd_seq_create_kernel_client(rdev->card, rdev->device, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device); - if (client < 0) { - err = client; - goto __error; - } + if (client < 0) + return client; rdev->client = client; /* create a port */ @@ -403,15 +404,11 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev) if (err < 0) { snd_seq_delete_kernel_client(client); rdev->client = -1; - goto __error; + return err; } rdev->port = pinfo->addr.port; - err = 0; /* success */ - - __error: - kfree(pinfo); - return err; + return 0; /* success */ } diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index 7f3fd8eb016f..654d620d0199 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -49,7 +49,7 @@ static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) sdrv->argsize == sdev->argsize; } -static struct bus_type snd_seq_bus_type = { +static const struct bus_type snd_seq_bus_type = { .name = "snd_seq", .match = snd_seq_bus_match, }; diff --git a/sound/core/sound.c b/sound/core/sound.c index df5571d98629..b9db9aa0bfcb 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -103,7 +103,7 @@ void *snd_lookup_minor_data(unsigned int minor, int type) if (minor >= ARRAY_SIZE(snd_minors)) return NULL; - mutex_lock(&sound_mutex); + guard(mutex)(&sound_mutex); mreg = snd_minors[minor]; if (mreg && mreg->type == type) { private_data = mreg->private_data; @@ -111,7 +111,6 @@ void *snd_lookup_minor_data(unsigned int minor, int type) get_device(&mreg->card_ptr->card_dev); } else private_data = NULL; - mutex_unlock(&sound_mutex); return private_data; } EXPORT_SYMBOL(snd_lookup_minor_data); @@ -150,17 +149,15 @@ static int snd_open(struct inode *inode, struct file *file) if (minor >= ARRAY_SIZE(snd_minors)) return -ENODEV; - mutex_lock(&sound_mutex); - mptr = snd_minors[minor]; - if (mptr == NULL) { - mptr = autoload_device(minor); - if (!mptr) { - mutex_unlock(&sound_mutex); - return -ENODEV; + scoped_guard(mutex, &sound_mutex) { + mptr = snd_minors[minor]; + if (mptr == NULL) { + mptr = autoload_device(minor); + if (!mptr) + return -ENODEV; } + new_fops = fops_get(mptr->f_ops); } - new_fops = fops_get(mptr->f_ops); - mutex_unlock(&sound_mutex); if (!new_fops) return -ENODEV; replace_fops(file, new_fops); @@ -269,7 +266,7 @@ int snd_register_device(int type, struct snd_card *card, int dev, preg->f_ops = f_ops; preg->private_data = private_data; preg->card_ptr = card; - mutex_lock(&sound_mutex); + guard(mutex)(&sound_mutex); minor = snd_find_free_minor(type, card, dev); if (minor < 0) { err = minor; @@ -284,7 +281,6 @@ int snd_register_device(int type, struct snd_card *card, int dev, snd_minors[minor] = preg; error: - mutex_unlock(&sound_mutex); if (err < 0) kfree(preg); return err; @@ -305,7 +301,7 @@ int snd_unregister_device(struct device *dev) int minor; struct snd_minor *preg; - mutex_lock(&sound_mutex); + guard(mutex)(&sound_mutex); for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { preg = snd_minors[minor]; if (preg && preg->dev == dev) { @@ -315,7 +311,6 @@ int snd_unregister_device(struct device *dev) break; } } - mutex_unlock(&sound_mutex); if (minor >= ARRAY_SIZE(snd_minors)) return -ENOENT; return 0; @@ -355,7 +350,7 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu int minor; struct snd_minor *mptr; - mutex_lock(&sound_mutex); + guard(mutex)(&sound_mutex); for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { mptr = snd_minors[minor]; if (!mptr) @@ -373,7 +368,6 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu snd_iprintf(buffer, "%3i: : %s\n", minor, snd_device_type_name(mptr->type)); } - mutex_unlock(&sound_mutex); } int __init snd_minor_info_init(void) diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c new file mode 100644 index 000000000000..eb90f62228c0 --- /dev/null +++ b/sound/core/sound_kunit.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sound core KUnit test + * Author: Ivan Orlov <ivan.orlov0322@gmail.com> + */ + +#include <kunit/test.h> +#include <sound/core.h> +#include <sound/pcm.h> + +#define SILENCE_BUFFER_MAX_FRAMES 260 +#define SILENCE_BUFFER_SIZE (sizeof(u64) * SILENCE_BUFFER_MAX_FRAMES) +#define SILENCE(...) { __VA_ARGS__ } +#define DEFINE_FORMAT(fmt, pbits, wd, endianness, signd, silence_arr) { \ + .format = SNDRV_PCM_FORMAT_##fmt, .physical_bits = pbits, \ + .width = wd, .le = endianness, .sd = signd, .silence = silence_arr, \ + .name = #fmt, \ +} + +#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1) +#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1 + +#define VALID_NAME "ValidName" +#define NAME_W_SPEC_CHARS "In%v@1id name" +#define NAME_W_SPACE "Test name" +#define NAME_W_SPACE_REMOVED "Testname" + +#define TEST_FIRST_COMPONENT "Component1" +#define TEST_SECOND_COMPONENT "Component2" + +struct snd_format_test_data { + snd_pcm_format_t format; + int physical_bits; + int width; + int le; + int sd; + unsigned char silence[8]; + unsigned char *name; +}; + +struct avail_test_data { + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t hw_ptr; + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t expected_avail; +}; + +static struct snd_format_test_data valid_fmt[] = { + DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()), + DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)), + DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()), + DEFINE_FORMAT(S16_BE, 16, 16, 0, 1, SILENCE()), + DEFINE_FORMAT(U16_LE, 16, 16, 1, 0, SILENCE(0x00, 0x80)), + DEFINE_FORMAT(U16_BE, 16, 16, 0, 0, SILENCE(0x80, 0x00)), + DEFINE_FORMAT(S24_LE, 32, 24, 1, 1, SILENCE()), + DEFINE_FORMAT(S24_BE, 32, 24, 0, 1, SILENCE()), + DEFINE_FORMAT(U24_LE, 32, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)), + DEFINE_FORMAT(U24_BE, 32, 24, 0, 0, SILENCE(0x00, 0x80, 0x00, 0x00)), + DEFINE_FORMAT(S32_LE, 32, 32, 1, 1, SILENCE()), + DEFINE_FORMAT(S32_BE, 32, 32, 0, 1, SILENCE()), + DEFINE_FORMAT(U32_LE, 32, 32, 1, 0, SILENCE(0x00, 0x00, 0x00, 0x80)), + DEFINE_FORMAT(U32_BE, 32, 32, 0, 0, SILENCE(0x80, 0x00, 0x00, 0x00)), + DEFINE_FORMAT(FLOAT_LE, 32, 32, 1, -1, SILENCE()), + DEFINE_FORMAT(FLOAT_BE, 32, 32, 0, -1, SILENCE()), + DEFINE_FORMAT(FLOAT64_LE, 64, 64, 1, -1, SILENCE()), + DEFINE_FORMAT(FLOAT64_BE, 64, 64, 0, -1, SILENCE()), + DEFINE_FORMAT(IEC958_SUBFRAME_LE, 32, 32, 1, -1, SILENCE()), + DEFINE_FORMAT(IEC958_SUBFRAME_BE, 32, 32, 0, -1, SILENCE()), + DEFINE_FORMAT(MU_LAW, 8, 8, -1, -1, SILENCE(0x7f)), + DEFINE_FORMAT(A_LAW, 8, 8, -1, -1, SILENCE(0x55)), + DEFINE_FORMAT(IMA_ADPCM, 4, 4, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_24, 3, 3, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_40, 5, 5, -1, -1, SILENCE()), + DEFINE_FORMAT(DSD_U8, 8, 8, 1, 0, SILENCE(0x69)), + DEFINE_FORMAT(DSD_U16_LE, 16, 16, 1, 0, SILENCE(0x69, 0x69)), + DEFINE_FORMAT(DSD_U32_LE, 32, 32, 1, 0, SILENCE(0x69, 0x69, 0x69, 0x69)), + DEFINE_FORMAT(DSD_U16_BE, 16, 16, 0, 0, SILENCE(0x69, 0x69)), + DEFINE_FORMAT(DSD_U32_BE, 32, 32, 0, 0, SILENCE(0x69, 0x69, 0x69, 0x69)), + DEFINE_FORMAT(S20_LE, 32, 20, 1, 1, SILENCE()), + DEFINE_FORMAT(S20_BE, 32, 20, 0, 1, SILENCE()), + DEFINE_FORMAT(U20_LE, 32, 20, 1, 0, SILENCE(0x00, 0x00, 0x08, 0x00)), + DEFINE_FORMAT(U20_BE, 32, 20, 0, 0, SILENCE(0x00, 0x08, 0x00, 0x00)), + DEFINE_FORMAT(S24_3LE, 24, 24, 1, 1, SILENCE()), + DEFINE_FORMAT(S24_3BE, 24, 24, 0, 1, SILENCE()), + DEFINE_FORMAT(U24_3LE, 24, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)), + DEFINE_FORMAT(U24_3BE, 24, 24, 0, 0, SILENCE(0x80, 0x00, 0x00)), + DEFINE_FORMAT(S20_3LE, 24, 20, 1, 1, SILENCE()), + DEFINE_FORMAT(S20_3BE, 24, 20, 0, 1, SILENCE()), + DEFINE_FORMAT(U20_3LE, 24, 20, 1, 0, SILENCE(0x00, 0x00, 0x08)), + DEFINE_FORMAT(U20_3BE, 24, 20, 0, 0, SILENCE(0x08, 0x00, 0x00)), + DEFINE_FORMAT(S18_3LE, 24, 18, 1, 1, SILENCE()), + DEFINE_FORMAT(S18_3BE, 24, 18, 0, 1, SILENCE()), + DEFINE_FORMAT(U18_3LE, 24, 18, 1, 0, SILENCE(0x00, 0x00, 0x02)), + DEFINE_FORMAT(U18_3BE, 24, 18, 0, 0, SILENCE(0x02, 0x00, 0x00)), + DEFINE_FORMAT(G723_24_1B, 8, 3, -1, -1, SILENCE()), + DEFINE_FORMAT(G723_40_1B, 8, 5, -1, -1, SILENCE()), +}; + +static void test_phys_format_size(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(valid_fmt[i].format), + valid_fmt[i].physical_bits); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_2), -EINVAL); +} + +static void test_format_width(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(valid_fmt[i].format), + valid_fmt[i].width); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL); +} + +static void test_format_signed(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_signed(valid_fmt[i].format), + valid_fmt[i].sd < 0 ? -EINVAL : valid_fmt[i].sd); + KUNIT_EXPECT_EQ(test, snd_pcm_format_unsigned(valid_fmt[i].format), + valid_fmt[i].sd < 0 ? -EINVAL : 1 - valid_fmt[i].sd); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL); +} + +static void test_format_endianness(struct kunit *test) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(valid_fmt[i].format), + valid_fmt[i].le < 0 ? -EINVAL : valid_fmt[i].le); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(valid_fmt[i].format), + valid_fmt[i].le < 0 ? -EINVAL : 1 - valid_fmt[i].le); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_2), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_1), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL); +} + +static void _test_fill_silence(struct kunit *test, struct snd_format_test_data *data, + u8 *buffer, size_t samples_count) +{ + size_t sample_bytes = data->physical_bits >> 3; + u32 i; + + KUNIT_ASSERT_EQ(test, snd_pcm_format_set_silence(data->format, buffer, samples_count), 0); + for (i = 0; i < samples_count * sample_bytes; i++) + KUNIT_EXPECT_EQ(test, buffer[i], data->silence[i % sample_bytes]); +} + +static void test_format_fill_silence(struct kunit *test) +{ + u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES }; + u8 *buffer; + u32 i, j; + + buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(buf_samples); i++) { + for (j = 0; j < ARRAY_SIZE(valid_fmt); j++) + _test_fill_silence(test, &valid_fmt[j], buffer, buf_samples[i]); + } + + KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(WRONG_FORMAT_1, buffer, 20), -EINVAL); + KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_LAST, buffer, 0), 0); +} + +static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size) +{ + snd_pcm_uframes_t boundary = buffer_size; + + while (boundary * 2 <= 0x7fffffffUL - buffer_size) + boundary *= 2; + return boundary; +} + +static struct avail_test_data p_avail_data[] = { + /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */ + { 128, 1000, 1129, 1073741824UL - 1 }, + /* + * buf_size + hw_ptr - appl_ptr >= boundary => + * => avail = buf_size + hw_ptr - appl_ptr - boundary + */ + { 128, 1073741824UL, 10, 118 }, + /* standard case: avail = buf_size + hw_ptr - appl_ptr */ + { 128, 1000, 1001, 127 }, +}; + +static void test_playback_avail(struct kunit *test) +{ + struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); + u32 i; + + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); + r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) { + r->buffer_size = p_avail_data[i].buffer_size; + r->boundary = calculate_boundary(r->buffer_size); + r->status->hw_ptr = p_avail_data[i].hw_ptr; + r->control->appl_ptr = p_avail_data[i].appl_ptr; + KUNIT_EXPECT_EQ(test, snd_pcm_playback_avail(r), p_avail_data[i].expected_avail); + } +} + +static struct avail_test_data c_avail_data[] = { + /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */ + { 128, 1000, 1001, 1073741824UL - 1 }, + /* standard case: avail = hw_ptr - appl_ptr */ + { 128, 1001, 1000, 1 }, +}; + +static void test_capture_avail(struct kunit *test) +{ + struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL); + u32 i; + + r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL); + r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL); + + for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) { + r->buffer_size = c_avail_data[i].buffer_size; + r->boundary = calculate_boundary(r->buffer_size); + r->status->hw_ptr = c_avail_data[i].hw_ptr; + r->control->appl_ptr = c_avail_data[i].appl_ptr; + KUNIT_EXPECT_EQ(test, snd_pcm_capture_avail(r), c_avail_data[i].expected_avail); + } +} + +static void test_card_set_id(struct kunit *test) +{ + struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + + snd_card_set_id(card, VALID_NAME); + KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME); + + /* clear the first id character so we can set it again */ + card->id[0] = '\0'; + snd_card_set_id(card, NAME_W_SPEC_CHARS); + KUNIT_EXPECT_STRNEQ(test, card->id, NAME_W_SPEC_CHARS); + + card->id[0] = '\0'; + snd_card_set_id(card, NAME_W_SPACE); + kunit_info(test, "%s", card->id); + KUNIT_EXPECT_STREQ(test, card->id, NAME_W_SPACE_REMOVED); +} + +static void test_pcm_format_name(struct kunit *test) +{ + u32 i; + const char *name; + + for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) { + name = snd_pcm_format_name(valid_fmt[i].format); + KUNIT_ASSERT_NOT_NULL_MSG(test, name, "Don't have name for %s", valid_fmt[i].name); + KUNIT_EXPECT_STREQ(test, name, valid_fmt[i].name); + } + + KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_1), "Unknown"); + KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_2), "Unknown"); +} + +static void test_card_add_component(struct kunit *test) +{ + struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL); + + snd_component_add(card, TEST_FIRST_COMPONENT); + KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT); + + snd_component_add(card, TEST_SECOND_COMPONENT); + KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT " " TEST_SECOND_COMPONENT); +} + +static struct kunit_case sound_utils_cases[] = { + KUNIT_CASE(test_phys_format_size), + KUNIT_CASE(test_format_width), + KUNIT_CASE(test_format_endianness), + KUNIT_CASE(test_format_signed), + KUNIT_CASE(test_format_fill_silence), + KUNIT_CASE(test_playback_avail), + KUNIT_CASE(test_capture_avail), + KUNIT_CASE(test_card_set_id), + KUNIT_CASE(test_pcm_format_name), + KUNIT_CASE(test_card_add_component), + {}, +}; + +static struct kunit_suite sound_utils_suite = { + .name = "sound-core-test", + .test_cases = sound_utils_cases, +}; + +kunit_test_suite(sound_utils_suite); +MODULE_AUTHOR("Ivan Orlov"); +MODULE_LICENSE("GPL"); diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 2751bf2ff61b..d65cc6fee2e6 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -29,7 +29,7 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) if (minor >= ARRAY_SIZE(snd_oss_minors)) return NULL; - mutex_lock(&sound_oss_mutex); + guard(mutex)(&sound_oss_mutex); mreg = snd_oss_minors[minor]; if (mreg && mreg->type == type) { private_data = mreg->private_data; @@ -37,7 +37,6 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) get_device(&mreg->card_ptr->card_dev); } else private_data = NULL; - mutex_unlock(&sound_oss_mutex); return private_data; } EXPORT_SYMBOL(snd_lookup_oss_minor_data); @@ -106,7 +105,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, preg->f_ops = f_ops; preg->private_data = private_data; preg->card_ptr = card; - mutex_lock(&sound_oss_mutex); + guard(mutex)(&sound_oss_mutex); snd_oss_minors[minor] = preg; minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); switch (minor_unit) { @@ -130,7 +129,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, goto __end; snd_oss_minors[track2] = preg; } - mutex_unlock(&sound_oss_mutex); return 0; __end: @@ -139,7 +137,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, if (register1 >= 0) unregister_sound_special(register1); snd_oss_minors[minor] = NULL; - mutex_unlock(&sound_oss_mutex); kfree(preg); return -EBUSY; } @@ -156,12 +153,10 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev) return 0; if (minor < 0) return minor; - mutex_lock(&sound_oss_mutex); + guard(mutex)(&sound_oss_mutex); mptr = snd_oss_minors[minor]; - if (mptr == NULL) { - mutex_unlock(&sound_oss_mutex); + if (mptr == NULL) return -ENOENT; - } switch (SNDRV_MINOR_OSS_DEVICE(minor)) { case SNDRV_MINOR_OSS_PCM: track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO); @@ -176,7 +171,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev) if (track2 >= 0) snd_oss_minors[track2] = NULL; snd_oss_minors[minor] = NULL; - mutex_unlock(&sound_oss_mutex); /* call unregister_sound_special() outside sound_oss_mutex; * otherwise may deadlock, as it can trigger the release of a card @@ -220,7 +214,7 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry, int minor; struct snd_minor *mptr; - mutex_lock(&sound_oss_mutex); + guard(mutex)(&sound_oss_mutex); for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) { mptr = snd_oss_minors[minor]; if (!mptr) @@ -233,7 +227,6 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry, snd_iprintf(buffer, "%3i: : %s\n", minor, snd_oss_device_type_name(mptr->type)); } - mutex_unlock(&sound_oss_mutex); } diff --git a/sound/core/timer.c b/sound/core/timer.c index e6e551d4a29e..15b07d09c4b7 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -224,14 +224,12 @@ static int check_matching_master_slave(struct snd_timer_instance *master, return -EBUSY; list_move_tail(&slave->open_list, &master->slave_list_head); master->timer->num_instances++; - spin_lock_irq(&slave_active_lock); - spin_lock(&master->timer->lock); + guard(spinlock_irq)(&slave_active_lock); + guard(spinlock)(&master->timer->lock); slave->master = master; slave->timer = master->timer; if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) list_add_tail(&slave->active_list, &master->slave_active_head); - spin_unlock(&master->timer->lock); - spin_unlock_irq(&slave_active_lock); return 1; } @@ -382,6 +380,25 @@ list_added: } EXPORT_SYMBOL(snd_timer_open); +/* remove slave links, called from snd_timer_close_locked() below */ +static void remove_slave_links(struct snd_timer_instance *timeri, + struct snd_timer *timer) +{ + struct snd_timer_instance *slave, *tmp; + + guard(spinlock_irq)(&slave_active_lock); + guard(spinlock)(&timer->lock); + timeri->timer = NULL; + list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) { + list_move_tail(&slave->open_list, &snd_timer_slave_list); + timer->num_instances--; + slave->master = NULL; + slave->timer = NULL; + list_del_init(&slave->ack_list); + list_del_init(&slave->active_list); + } +} + /* * close a timer instance * call this with register_mutex down. @@ -390,12 +407,10 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, struct device **card_devp_to_put) { struct snd_timer *timer = timeri->timer; - struct snd_timer_instance *slave, *tmp; if (timer) { - spin_lock_irq(&timer->lock); + guard(spinlock)(&timer->lock); timeri->flags |= SNDRV_TIMER_IFLG_DEAD; - spin_unlock_irq(&timer->lock); } if (!list_empty(&timeri->open_list)) { @@ -418,21 +433,7 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, } spin_unlock_irq(&timer->lock); - /* remove slave links */ - spin_lock_irq(&slave_active_lock); - spin_lock(&timer->lock); - timeri->timer = NULL; - list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, - open_list) { - list_move_tail(&slave->open_list, &snd_timer_slave_list); - timer->num_instances--; - slave->master = NULL; - slave->timer = NULL; - list_del_init(&slave->ack_list); - list_del_init(&slave->active_list); - } - spin_unlock(&timer->lock); - spin_unlock_irq(&slave_active_lock); + remove_slave_links(timeri, timer); /* slave doesn't need to release timer resources below */ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) @@ -459,9 +460,8 @@ void snd_timer_close(struct snd_timer_instance *timeri) if (snd_BUG_ON(!timeri)) return; - mutex_lock(®ister_mutex); - snd_timer_close_locked(timeri, &card_dev_to_put); - mutex_unlock(®ister_mutex); + scoped_guard(mutex, ®ister_mutex) + snd_timer_close_locked(timeri, &card_dev_to_put); /* put_device() is called after unlock for avoiding deadlock */ if (card_dev_to_put) put_device(card_dev_to_put); @@ -480,15 +480,13 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) { struct snd_timer * timer; unsigned long ret = 0; - unsigned long flags; if (timeri == NULL) return 0; timer = timeri->timer; if (timer) { - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); ret = snd_timer_hw_resolution(timer); - spin_unlock_irqrestore(&timer->lock, flags); } return ret; } @@ -532,26 +530,19 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, { struct snd_timer *timer; int result; - unsigned long flags; timer = timeri->timer; if (!timer) return -EINVAL; - spin_lock_irqsave(&timer->lock, flags); - if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { - result = -EINVAL; - goto unlock; - } - if (timer->card && timer->card->shutdown) { - result = -ENODEV; - goto unlock; - } + guard(spinlock_irqsave)(&timer->lock); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) + return -EINVAL; + if (timer->card && timer->card->shutdown) + return -ENODEV; if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | - SNDRV_TIMER_IFLG_START)) { - result = -EBUSY; - goto unlock; - } + SNDRV_TIMER_IFLG_START)) + return -EBUSY; if (start) timeri->ticks = timeri->cticks = ticks; @@ -577,8 +568,6 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, } snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : SNDRV_TIMER_EVENT_CONTINUE); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); return result; } @@ -586,53 +575,38 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, static int snd_timer_start_slave(struct snd_timer_instance *timeri, bool start) { - unsigned long flags; - int err; - - spin_lock_irqsave(&slave_active_lock, flags); - if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { - err = -EINVAL; - goto unlock; - } - if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { - err = -EBUSY; - goto unlock; - } + guard(spinlock_irqsave)(&slave_active_lock); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) + return -EBUSY; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; if (timeri->master && timeri->timer) { - spin_lock(&timeri->timer->lock); + guard(spinlock)(&timeri->timer->lock); list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : SNDRV_TIMER_EVENT_CONTINUE); - spin_unlock(&timeri->timer->lock); } - err = 1; /* delayed start */ - unlock: - spin_unlock_irqrestore(&slave_active_lock, flags); - return err; + return 1; /* delayed start */ } /* stop/pause a master timer */ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) { struct snd_timer *timer; - int result = 0; - unsigned long flags; timer = timeri->timer; if (!timer) return -EINVAL; - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | - SNDRV_TIMER_IFLG_START))) { - result = -EBUSY; - goto unlock; - } + SNDRV_TIMER_IFLG_START))) + return -EBUSY; if (timer->card && timer->card->shutdown) - goto unlock; + return 0; if (stop) { timeri->cticks = timeri->ticks; timeri->pticks = 0; @@ -656,30 +630,25 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : SNDRV_TIMER_EVENT_PAUSE); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); - return result; + return 0; } /* stop/pause a slave timer */ static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) { - unsigned long flags; bool running; - spin_lock_irqsave(&slave_active_lock, flags); + guard(spinlock_irqsave)(&slave_active_lock); running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING; timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; if (timeri->timer) { - spin_lock(&timeri->timer->lock); + guard(spinlock)(&timeri->timer->lock); list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); if (running) snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : SNDRV_TIMER_EVENT_PAUSE); - spin_unlock(&timeri->timer->lock); } - spin_unlock_irqrestore(&slave_active_lock, flags); return running ? 0 : -EBUSY; } @@ -804,12 +773,9 @@ static void snd_timer_process_callbacks(struct snd_timer *timer, static void snd_timer_clear_callbacks(struct snd_timer *timer, struct list_head *head) { - unsigned long flags; - - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); while (!list_empty(head)) list_del_init(head->next); - spin_unlock_irqrestore(&timer->lock, flags); } /* @@ -819,16 +785,14 @@ static void snd_timer_clear_callbacks(struct snd_timer *timer, static void snd_timer_work(struct work_struct *work) { struct snd_timer *timer = container_of(work, struct snd_timer, task_work); - unsigned long flags; if (timer->card && timer->card->shutdown) { snd_timer_clear_callbacks(timer, &timer->sack_list_head); return; } - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); snd_timer_process_callbacks(timer, &timer->sack_list_head); - spin_unlock_irqrestore(&timer->lock, flags); } /* @@ -842,8 +806,6 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) struct snd_timer_instance *ti, *ts, *tmp; unsigned long resolution; struct list_head *ack_list_head; - unsigned long flags; - bool use_work = false; if (timer == NULL) return; @@ -853,7 +815,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) return; } - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); /* remember the current resolution */ resolution = snd_timer_hw_resolution(timer); @@ -919,10 +881,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) snd_timer_process_callbacks(timer, &timer->ack_list_head); /* do we have any slow callbacks? */ - use_work = !list_empty(&timer->sack_list_head); - spin_unlock_irqrestore(&timer->lock, flags); - - if (use_work) + if (!list_empty(&timer->sack_list_head)) queue_work(system_highpri_wq, &timer->task_work); } EXPORT_SYMBOL(snd_timer_interrupt); @@ -988,7 +947,7 @@ static int snd_timer_free(struct snd_timer *timer) if (!timer) return 0; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); if (! list_empty(&timer->open_list_head)) { struct list_head *p, *n; struct snd_timer_instance *ti; @@ -1000,7 +959,6 @@ static int snd_timer_free(struct snd_timer *timer) } } list_del(&timer->device_list); - mutex_unlock(®ister_mutex); if (timer->private_free) timer->private_free(timer); @@ -1025,7 +983,7 @@ static int snd_timer_dev_register(struct snd_device *dev) !timer->hw.resolution && timer->hw.c_resolution == NULL) return -EINVAL; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_for_each_entry(timer1, &snd_timer_list, device_list) { if (timer1->tmr_class > timer->tmr_class) break; @@ -1046,11 +1004,9 @@ static int snd_timer_dev_register(struct snd_device *dev) if (timer1->tmr_subdevice < timer->tmr_subdevice) continue; /* conflicts.. */ - mutex_unlock(®ister_mutex); return -EBUSY; } list_add_tail(&timer->device_list, &timer1->device_list); - mutex_unlock(®ister_mutex); return 0; } @@ -1059,20 +1015,18 @@ static int snd_timer_dev_disconnect(struct snd_device *device) struct snd_timer *timer = device->device_data; struct snd_timer_instance *ti; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_del_init(&timer->device_list); /* wake up pending sleepers */ list_for_each_entry(ti, &timer->open_list_head, open_list) { if (ti->disconnect) ti->disconnect(ti); } - mutex_unlock(®ister_mutex); return 0; } void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) { - unsigned long flags; unsigned long resolution = 0; struct snd_timer_instance *ti, *ts; @@ -1083,7 +1037,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tst if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || event > SNDRV_TIMER_EVENT_MRESUME)) return; - spin_lock_irqsave(&timer->lock, flags); + guard(spinlock_irqsave)(&timer->lock); if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE || event == SNDRV_TIMER_EVENT_MRESUME) @@ -1095,7 +1049,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tst if (ts->ccallback) ts->ccallback(ts, event, tstamp, resolution); } - spin_unlock_irqrestore(&timer->lock, flags); } EXPORT_SYMBOL(snd_timer_notify); @@ -1248,7 +1201,7 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, struct snd_timer_instance *ti; unsigned long resolution; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); list_for_each_entry(timer, &snd_timer_list, device_list) { if (timer->card && timer->card->shutdown) continue; @@ -1270,9 +1223,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, timer->tmr_device, timer->tmr_subdevice); } snd_iprintf(buffer, "%s :", timer->name); - spin_lock_irq(&timer->lock); - resolution = snd_timer_hw_resolution(timer); - spin_unlock_irq(&timer->lock); + scoped_guard(spinlock_irq, &timer->lock) + resolution = snd_timer_hw_resolution(timer); if (resolution) snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", resolution / 1000, @@ -1288,7 +1240,6 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, SNDRV_TIMER_IFLG_RUNNING)) ? "running" : "stopped"); } - mutex_unlock(®ister_mutex); } static struct snd_info_entry *snd_timer_proc_entry; @@ -1329,7 +1280,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, struct snd_timer_read *r; int prev; - spin_lock(&tu->qlock); + guard(spinlock)(&tu->qlock); if (tu->qused > 0) { prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; r = &tu->queue[prev]; @@ -1348,7 +1299,6 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, tu->qused++; } __wake: - spin_unlock(&tu->qlock); snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } @@ -1372,7 +1322,6 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, { struct snd_timer_user *tu = timeri->callback_data; struct snd_timer_tread64 r1; - unsigned long flags; if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) @@ -1384,9 +1333,8 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, r1.tstamp_sec = tstamp->tv_sec; r1.tstamp_nsec = tstamp->tv_nsec; r1.val = resolution; - spin_lock_irqsave(&tu->qlock, flags); - snd_timer_user_append_to_tqueue(tu, &r1); - spin_unlock_irqrestore(&tu->qlock, flags); + scoped_guard(spinlock_irqsave, &tu->qlock) + snd_timer_user_append_to_tqueue(tu, &r1); snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } @@ -1410,51 +1358,48 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, memset(&r1, 0, sizeof(r1)); memset(&tstamp, 0, sizeof(tstamp)); - spin_lock(&tu->qlock); - if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | - (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { - spin_unlock(&tu->qlock); - return; - } - if (tu->last_resolution != resolution || ticks > 0) { - if (timer_tstamp_monotonic) - ktime_get_ts64(&tstamp); - else - ktime_get_real_ts64(&tstamp); - } - if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && - tu->last_resolution != resolution) { - r1.event = SNDRV_TIMER_EVENT_RESOLUTION; + scoped_guard(spinlock, &tu->qlock) { + if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | + (1 << SNDRV_TIMER_EVENT_TICK))) == 0) + return; + if (tu->last_resolution != resolution || ticks > 0) { + if (timer_tstamp_monotonic) + ktime_get_ts64(&tstamp); + else + ktime_get_real_ts64(&tstamp); + } + if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && + tu->last_resolution != resolution) { + r1.event = SNDRV_TIMER_EVENT_RESOLUTION; + r1.tstamp_sec = tstamp.tv_sec; + r1.tstamp_nsec = tstamp.tv_nsec; + r1.val = resolution; + snd_timer_user_append_to_tqueue(tu, &r1); + tu->last_resolution = resolution; + append++; + } + if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) + break; + if (ticks == 0) + break; + if (tu->qused > 0) { + prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; + r = &tu->tqueue[prev]; + if (r->event == SNDRV_TIMER_EVENT_TICK) { + r->tstamp_sec = tstamp.tv_sec; + r->tstamp_nsec = tstamp.tv_nsec; + r->val += ticks; + append++; + break; + } + } + r1.event = SNDRV_TIMER_EVENT_TICK; r1.tstamp_sec = tstamp.tv_sec; r1.tstamp_nsec = tstamp.tv_nsec; - r1.val = resolution; + r1.val = ticks; snd_timer_user_append_to_tqueue(tu, &r1); - tu->last_resolution = resolution; append++; } - if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) - goto __wake; - if (ticks == 0) - goto __wake; - if (tu->qused > 0) { - prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; - r = &tu->tqueue[prev]; - if (r->event == SNDRV_TIMER_EVENT_TICK) { - r->tstamp_sec = tstamp.tv_sec; - r->tstamp_nsec = tstamp.tv_nsec; - r->val += ticks; - append++; - goto __wake; - } - } - r1.event = SNDRV_TIMER_EVENT_TICK; - r1.tstamp_sec = tstamp.tv_sec; - r1.tstamp_nsec = tstamp.tv_nsec; - r1.val = ticks; - snd_timer_user_append_to_tqueue(tu, &r1); - append++; - __wake: - spin_unlock(&tu->qlock); if (append == 0) return; snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); @@ -1476,14 +1421,13 @@ static int realloc_user_queue(struct snd_timer_user *tu, int size) return -ENOMEM; } - spin_lock_irq(&tu->qlock); + guard(spinlock_irq)(&tu->qlock); kfree(tu->queue); kfree(tu->tqueue); tu->queue_size = size; tu->queue = queue; tu->tqueue = tqueue; tu->qhead = tu->qtail = tu->qused = 0; - spin_unlock_irq(&tu->qlock); return 0; } @@ -1519,12 +1463,12 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) if (file->private_data) { tu = file->private_data; file->private_data = NULL; - mutex_lock(&tu->ioctl_lock); - if (tu->timeri) { - snd_timer_close(tu->timeri); - snd_timer_instance_free(tu->timeri); + scoped_guard(mutex, &tu->ioctl_lock) { + if (tu->timeri) { + snd_timer_close(tu->timeri); + snd_timer_instance_free(tu->timeri); + } } - mutex_unlock(&tu->ioctl_lock); snd_fasync_free(tu->fasync); kfree(tu->queue); kfree(tu->tqueue); @@ -1559,7 +1503,7 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) if (copy_from_user(&id, _tid, sizeof(id))) return -EFAULT; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); if (id.dev_class < 0) { /* first item */ if (list_empty(&snd_timer_list)) snd_timer_user_zero_id(&id); @@ -1636,7 +1580,6 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) snd_timer_user_zero_id(&id); } } - mutex_unlock(®ister_mutex); if (copy_to_user(_tid, &id, sizeof(*_tid))) return -EFAULT; return 0; @@ -1645,70 +1588,54 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) static int snd_timer_user_ginfo(struct file *file, struct snd_timer_ginfo __user *_ginfo) { - struct snd_timer_ginfo *ginfo; + struct snd_timer_ginfo *ginfo __free(kfree) = NULL; struct snd_timer_id tid; struct snd_timer *t; struct list_head *p; - int err = 0; ginfo = memdup_user(_ginfo, sizeof(*ginfo)); if (IS_ERR(ginfo)) - return PTR_ERR(ginfo); + return PTR_ERR(no_free_ptr(ginfo)); tid = ginfo->tid; memset(ginfo, 0, sizeof(*ginfo)); ginfo->tid = tid; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); t = snd_timer_find(&tid); - if (t != NULL) { - ginfo->card = t->card ? t->card->number : -1; - if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) - ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; - strscpy(ginfo->id, t->id, sizeof(ginfo->id)); - strscpy(ginfo->name, t->name, sizeof(ginfo->name)); - spin_lock_irq(&t->lock); + if (!t) + return -ENODEV; + ginfo->card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; + strscpy(ginfo->id, t->id, sizeof(ginfo->id)); + strscpy(ginfo->name, t->name, sizeof(ginfo->name)); + scoped_guard(spinlock_irq, &t->lock) ginfo->resolution = snd_timer_hw_resolution(t); - spin_unlock_irq(&t->lock); - if (t->hw.resolution_min > 0) { - ginfo->resolution_min = t->hw.resolution_min; - ginfo->resolution_max = t->hw.resolution_max; - } - list_for_each(p, &t->open_list_head) { - ginfo->clients++; - } - } else { - err = -ENODEV; + if (t->hw.resolution_min > 0) { + ginfo->resolution_min = t->hw.resolution_min; + ginfo->resolution_max = t->hw.resolution_max; } - mutex_unlock(®ister_mutex); - if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) - err = -EFAULT; - kfree(ginfo); - return err; + list_for_each(p, &t->open_list_head) { + ginfo->clients++; + } + if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) + return -EFAULT; + return 0; } static int timer_set_gparams(struct snd_timer_gparams *gparams) { struct snd_timer *t; - int err; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); t = snd_timer_find(&gparams->tid); - if (!t) { - err = -ENODEV; - goto _error; - } - if (!list_empty(&t->open_list_head)) { - err = -EBUSY; - goto _error; - } - if (!t->hw.set_period) { - err = -ENOSYS; - goto _error; - } - err = t->hw.set_period(t, gparams->period_num, gparams->period_den); -_error: - mutex_unlock(®ister_mutex); - return err; + if (!t) + return -ENODEV; + if (!list_empty(&t->open_list_head)) + return -EBUSY; + if (!t->hw.set_period) + return -ENOSYS; + return t->hw.set_period(t, gparams->period_num, gparams->period_den); } static int snd_timer_user_gparams(struct file *file, @@ -1734,10 +1661,10 @@ static int snd_timer_user_gstatus(struct file *file, tid = gstatus.tid; memset(&gstatus, 0, sizeof(gstatus)); gstatus.tid = tid; - mutex_lock(®ister_mutex); + guard(mutex)(®ister_mutex); t = snd_timer_find(&tid); if (t != NULL) { - spin_lock_irq(&t->lock); + guard(spinlock_irq)(&t->lock); gstatus.resolution = snd_timer_hw_resolution(t); if (t->hw.precise_resolution) { t->hw.precise_resolution(t, &gstatus.resolution_num, @@ -1746,11 +1673,9 @@ static int snd_timer_user_gstatus(struct file *file, gstatus.resolution_num = gstatus.resolution; gstatus.resolution_den = 1000000000uL; } - spin_unlock_irq(&t->lock); } else { err = -ENODEV; } - mutex_unlock(®ister_mutex); if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) err = -EFAULT; return err; @@ -1804,9 +1729,8 @@ static int snd_timer_user_info(struct file *file, struct snd_timer_info __user *_info) { struct snd_timer_user *tu; - struct snd_timer_info *info; + struct snd_timer_info *info __free(kfree) = NULL; struct snd_timer *t; - int err = 0; tu = file->private_data; if (!tu->timeri) @@ -1823,13 +1747,11 @@ static int snd_timer_user_info(struct file *file, info->flags |= SNDRV_TIMER_FLG_SLAVE; strscpy(info->id, t->id, sizeof(info->id)); strscpy(info->name, t->name, sizeof(info->name)); - spin_lock_irq(&t->lock); - info->resolution = snd_timer_hw_resolution(t); - spin_unlock_irq(&t->lock); + scoped_guard(spinlock_irq, &t->lock) + info->resolution = snd_timer_hw_resolution(t); if (copy_to_user(_info, info, sizeof(*_info))) - err = -EFAULT; - kfree(info); - return err; + return -EFAULT; + return 0; } static int snd_timer_user_params(struct file *file, @@ -1887,45 +1809,47 @@ static int snd_timer_user_params(struct file *file, goto _end; } snd_timer_stop(tu->timeri); - spin_lock_irq(&t->lock); - tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| - SNDRV_TIMER_IFLG_EXCLUSIVE| - SNDRV_TIMER_IFLG_EARLY_EVENT); - if (params.flags & SNDRV_TIMER_PSFLG_AUTO) - tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; - if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) - tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; - if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) - tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; - spin_unlock_irq(&t->lock); + scoped_guard(spinlock_irq, &t->lock) { + tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| + SNDRV_TIMER_IFLG_EXCLUSIVE| + SNDRV_TIMER_IFLG_EARLY_EVENT); + if (params.flags & SNDRV_TIMER_PSFLG_AUTO) + tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; + if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) + tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; + if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) + tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; + } if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { err = realloc_user_queue(tu, params.queue_size); if (err < 0) goto _end; } - spin_lock_irq(&tu->qlock); - tu->qhead = tu->qtail = tu->qused = 0; - if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { - if (tu->tread) { - struct snd_timer_tread64 tread; - memset(&tread, 0, sizeof(tread)); - tread.event = SNDRV_TIMER_EVENT_EARLY; - tread.tstamp_sec = 0; - tread.tstamp_nsec = 0; - tread.val = 0; - snd_timer_user_append_to_tqueue(tu, &tread); - } else { - struct snd_timer_read *r = &tu->queue[0]; - r->resolution = 0; - r->ticks = 0; - tu->qused++; - tu->qtail++; + scoped_guard(spinlock_irq, &tu->qlock) { + tu->qhead = tu->qtail = tu->qused = 0; + if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { + if (tu->tread) { + struct snd_timer_tread64 tread; + + memset(&tread, 0, sizeof(tread)); + tread.event = SNDRV_TIMER_EVENT_EARLY; + tread.tstamp_sec = 0; + tread.tstamp_nsec = 0; + tread.val = 0; + snd_timer_user_append_to_tqueue(tu, &tread); + } else { + struct snd_timer_read *r = &tu->queue[0]; + + r->resolution = 0; + r->ticks = 0; + tu->qused++; + tu->qtail++; + } } + tu->filter = params.filter; + tu->ticks = params.ticks; } - tu->filter = params.filter; - tu->ticks = params.ticks; - spin_unlock_irq(&tu->qlock); err = 0; _end: if (copy_to_user(_params, ¶ms, sizeof(params))) @@ -1948,9 +1872,8 @@ static int snd_timer_user_status32(struct file *file, status.resolution = snd_timer_resolution(tu->timeri); status.lost = tu->timeri->lost; status.overrun = tu->overrun; - spin_lock_irq(&tu->qlock); - status.queue = tu->qused; - spin_unlock_irq(&tu->qlock); + scoped_guard(spinlock_irq, &tu->qlock) + status.queue = tu->qused; if (copy_to_user(_status, &status, sizeof(status))) return -EFAULT; return 0; @@ -1971,9 +1894,8 @@ static int snd_timer_user_status64(struct file *file, status.resolution = snd_timer_resolution(tu->timeri); status.lost = tu->timeri->lost; status.overrun = tu->overrun; - spin_lock_irq(&tu->qlock); - status.queue = tu->qused; - spin_unlock_irq(&tu->qlock); + scoped_guard(spinlock_irq, &tu->qlock) + status.queue = tu->qused; if (copy_to_user(_status, &status, sizeof(status))) return -EFAULT; return 0; @@ -2131,12 +2053,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_timer_user *tu = file->private_data; - long ret; - mutex_lock(&tu->ioctl_lock); - ret = __snd_timer_user_ioctl(file, cmd, arg, false); - mutex_unlock(&tu->ioctl_lock); - return ret; + guard(mutex)(&tu->ioctl_lock); + return __snd_timer_user_ioctl(file, cmd, arg, false); } static int snd_timer_user_fasync(int fd, struct file * file, int on) @@ -2263,12 +2182,11 @@ static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) poll_wait(file, &tu->qchange_sleep, wait); mask = 0; - spin_lock_irq(&tu->qlock); + guard(spinlock_irq)(&tu->qlock); if (tu->qused) mask |= EPOLLIN | EPOLLRDNORM; if (tu->disconnected) mask |= EPOLLERR; - spin_unlock_irq(&tu->qlock); return mask; } diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index ee973b7b8044..4ae9eaeb5afb 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -115,10 +115,7 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_timer_user *tu = file->private_data; - long ret; - mutex_lock(&tu->ioctl_lock); - ret = __snd_timer_user_ioctl_compat(file, cmd, arg); - mutex_unlock(&tu->ioctl_lock); - return ret; + guard(mutex)(&tu->ioctl_lock); + return __snd_timer_user_ioctl_compat(file, cmd, arg); } diff --git a/sound/core/ump.c b/sound/core/ump.c index fe7911498cc4..fd6a68a54278 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -985,13 +985,11 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream; int group = ump->legacy_mapping[substream->number]; - int err = 0; + int err; - mutex_lock(&ump->open_mutex); - if (ump->legacy_substreams[dir][group]) { - err = -EBUSY; - goto unlock; - } + guard(mutex)(&ump->open_mutex); + if (ump->legacy_substreams[dir][group]) + return -EBUSY; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!ump->legacy_out_opens) { err = snd_rawmidi_kernel_open(&ump->core, 0, @@ -999,17 +997,14 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) SNDRV_RAWMIDI_LFLG_APPEND, &ump->legacy_out_rfile); if (err < 0) - goto unlock; + return err; } ump->legacy_out_opens++; snd_ump_convert_reset(&ump->out_cvts[group]); } - spin_lock_irq(&ump->legacy_locks[dir]); + guard(spinlock_irq)(&ump->legacy_locks[dir]); ump->legacy_substreams[dir][group] = substream; - spin_unlock_irq(&ump->legacy_locks[dir]); - unlock: - mutex_unlock(&ump->open_mutex); - return err; + return 0; } static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) @@ -1018,15 +1013,13 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) int dir = substream->stream; int group = ump->legacy_mapping[substream->number]; - mutex_lock(&ump->open_mutex); - spin_lock_irq(&ump->legacy_locks[dir]); - ump->legacy_substreams[dir][group] = NULL; - spin_unlock_irq(&ump->legacy_locks[dir]); + guard(mutex)(&ump->open_mutex); + scoped_guard(spinlock_irq, &ump->legacy_locks[dir]) + ump->legacy_substreams[dir][group] = NULL; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!--ump->legacy_out_opens) snd_rawmidi_kernel_release(&ump->legacy_out_rfile); } - mutex_unlock(&ump->open_mutex); return 0; } @@ -1078,12 +1071,11 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT; unsigned char c; int group, size = 0; - unsigned long flags; if (!ump->out_cvts || !ump->legacy_out_opens) return 0; - spin_lock_irqsave(&ump->legacy_locks[dir], flags); + guard(spinlock_irqsave)(&ump->legacy_locks[dir]); for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) { substream = ump->legacy_substreams[dir][group]; if (!substream) @@ -1099,7 +1091,6 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, break; } } - spin_unlock_irqrestore(&ump->legacy_locks[dir], flags); return size; } @@ -1109,18 +1100,16 @@ static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, struct snd_rawmidi_substream *substream; unsigned char buf[16]; unsigned char group; - unsigned long flags; const int dir = SNDRV_RAWMIDI_STREAM_INPUT; int size; size = snd_ump_convert_from_ump(src, buf, &group); if (size <= 0) return; - spin_lock_irqsave(&ump->legacy_locks[dir], flags); + guard(spinlock_irqsave)(&ump->legacy_locks[dir]); substream = ump->legacy_substreams[dir][group]; if (substream) snd_rawmidi_receive(substream, buf, size); - spin_unlock_irqrestore(&ump->legacy_locks[dir], flags); } /* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */ diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 378d2c7c3d4a..04a57f7be6ea 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -56,7 +56,7 @@ struct link_follower { static int follower_update(struct link_follower *follower) { - struct snd_ctl_elem_value *uctl; + struct snd_ctl_elem_value *uctl __free(kfree) = NULL; int err, ch; uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); @@ -65,18 +65,16 @@ static int follower_update(struct link_follower *follower) uctl->id = follower->follower.id; err = follower->follower.get(&follower->follower, uctl); if (err < 0) - goto error; + return err; for (ch = 0; ch < follower->info.count; ch++) follower->vals[ch] = uctl->value.integer.value[ch]; - error: - kfree(uctl); - return err < 0 ? err : 0; + return 0; } /* get the follower ctl info and save the initial values */ static int follower_init(struct link_follower *follower) { - struct snd_ctl_elem_info *uinfo; + struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; int err; if (follower->info.count) { @@ -91,22 +89,18 @@ static int follower_init(struct link_follower *follower) return -ENOMEM; uinfo->id = follower->follower.id; err = follower->follower.info(&follower->follower, uinfo); - if (err < 0) { - kfree(uinfo); + if (err < 0) return err; - } follower->info.type = uinfo->type; follower->info.count = uinfo->count; if (follower->info.count > 2 || (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { pr_err("ALSA: vmaster: invalid follower element\n"); - kfree(uinfo); return -EINVAL; } follower->info.min_val = uinfo->value.integer.min; follower->info.max_val = uinfo->value.integer.max; - kfree(uinfo); return follower_update(follower); } @@ -341,7 +335,7 @@ static int master_get(struct snd_kcontrol *kcontrol, static int sync_followers(struct link_master *master, int old_val, int new_val) { struct link_follower *follower; - struct snd_ctl_elem_value *uval; + struct snd_ctl_elem_value *uval __free(kfree) = NULL; uval = kmalloc(sizeof(*uval), GFP_KERNEL); if (!uval) @@ -353,7 +347,6 @@ static int sync_followers(struct link_master *master, int old_val, int new_val) master->val = new_val; follower_put_val(follower, uval); } - kfree(uval); return 0; } diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 1c65e0a3b13c..892c4e29c0a3 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1830,7 +1830,6 @@ static int loopback_probe(struct platform_device *devptr) return 0; } -#ifdef CONFIG_PM_SLEEP static int loopback_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); @@ -1847,11 +1846,7 @@ static int loopback_resume(struct device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); -#define LOOPBACK_PM_OPS &loopback_pm -#else -#define LOOPBACK_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume); #define SND_LOOPBACK_DRIVER "snd_aloop" @@ -1859,7 +1854,7 @@ static struct platform_driver loopback_driver = { .probe = loopback_probe, .driver = { .name = SND_LOOPBACK_DRIVER, - .pm = LOOPBACK_PM_OPS, + .pm = &loopback_pm, }, }; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 4317677ba24a..52ff6ac3f743 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1098,7 +1098,6 @@ static int snd_dummy_probe(struct platform_device *devptr) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_dummy_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); @@ -1115,11 +1114,7 @@ static int snd_dummy_resume(struct device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume); -#define SND_DUMMY_PM_OPS &snd_dummy_pm -#else -#define SND_DUMMY_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume); #define SND_DUMMY_DRIVER "snd_dummy" @@ -1127,7 +1122,7 @@ static struct platform_driver snd_dummy_driver = { .probe = snd_dummy_probe, .driver = { .name = SND_DUMMY_DRIVER, - .pm = SND_DUMMY_PM_OPS, + .pm = &snd_dummy_pm, }, }; diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index c7be1c395bcb..7195cb49e00f 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -176,7 +176,6 @@ static void pcsp_stop_beep(struct snd_pcsp *chip) pcspkr_stop_sound(); } -#ifdef CONFIG_PM_SLEEP static int pcsp_suspend(struct device *dev) { struct snd_pcsp *chip = dev_get_drvdata(dev); @@ -184,11 +183,7 @@ static int pcsp_suspend(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL); -#define PCSP_PM_OPS &pcsp_pm -#else -#define PCSP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL); static void pcsp_shutdown(struct platform_device *dev) { @@ -199,7 +194,7 @@ static void pcsp_shutdown(struct platform_device *dev) static struct platform_driver pcsp_platform_driver = { .driver = { .name = "pcspkr", - .pm = PCSP_PM_OPS, + .pm = &pcsp_pm, }, .probe = pcsp_probe, .shutdown = pcsp_shutdown, diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 22b6c779682a..5973c25c2add 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -175,6 +175,8 @@ config SND_FIREWIRE_MOTU * 8pre * 828mk3 (FireWire only) * 828mk3 (Hybrid) + * 896mk3 (FireWire only) + * 896mk3 (Hybrid) * Ultralite mk3 (FireWire only) * Ultralite mk3 (Hybrid) * Traveler mk3 diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 7be17bca257f..c9f153f85ae6 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -773,10 +773,14 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, } else { unsigned int dbc_interval; - if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) - dbc_interval = s->ctx_data.tx.dbc_interval; - else - dbc_interval = *data_blocks; + if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) { + if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) + dbc_interval = s->ctx_data.tx.dbc_interval; + else + dbc_interval = *data_blocks; + } else { + dbc_interval = payload_length / sizeof(__be32); + } lost = dbc != ((*data_block_counter + dbc_interval) & 0xff); } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index b7ff44751ab9..a1ed2e80f91a 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -37,6 +37,9 @@ * the value of current SYT_INTERVAL; e.g. initial value is not zero. * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff. * For incoming packet, the value in SYT field of CIP is not handled. + * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with + * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets + * in CIP payload, instead of the number of accumulated data blocks. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -51,6 +54,7 @@ enum cip_flags { CIP_NO_HEADER = 0x100, CIP_UNALIGHED_DBC = 0x200, CIP_UNAWARE_SYT = 0x400, + CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800, }; /** diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index 8a0426920a76..7254fdfe046a 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -261,6 +261,7 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) if (motu->spec == &snd_motu_spec_828mk3_fw || motu->spec == &snd_motu_spec_828mk3_hybrid || + motu->spec == &snd_motu_spec_896mk3 || motu->spec == &snd_motu_spec_traveler_mk3 || motu->spec == &snd_motu_spec_track16) return detect_packet_formats_with_opt_ifaces(motu, data); @@ -288,6 +289,14 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. }; +const struct snd_motu_spec snd_motu_spec_896mk3 = { + .name = "896mk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_COMMAND_DSP, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {18, 14, 10}, +}; + const struct snd_motu_spec snd_motu_spec_traveler_mk3 = { .name = "TravelerMk3", .protocol_version = SND_MOTU_PROTOCOL_V3, diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index d73599eb7d5a..d14ab5dd5bea 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -168,10 +168,12 @@ static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3), SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid. + SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16), SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 3b1dc98a7be0..c66be0a89ccf 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -138,6 +138,7 @@ extern const struct snd_motu_spec snd_motu_spec_8pre; extern const struct snd_motu_spec snd_motu_spec_828mk3_fw; extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid; +extern const struct snd_motu_spec snd_motu_spec_896mk3; extern const struct snd_motu_spec snd_motu_spec_traveler_mk3; extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; extern const struct snd_motu_spec snd_motu_spec_audio_express; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index f4a702def397..00f7feb91f92 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -177,6 +177,8 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) flags |= CIP_JUMBO_PAYLOAD; if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS) flags |= CIP_WRONG_DBS; + if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS) + flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS; } else { conn = &oxfw->in_conn; c_dir = CMP_INPUT; @@ -486,26 +488,57 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, struct snd_oxfw_stream_formation *formation) { - u8 *format; - unsigned int len; int err; - len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; - format = kmalloc(len, GFP_KERNEL); - if (format == NULL) - return -ENOMEM; + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + u8 *format; + unsigned int len; - err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); - if (err < 0) - goto end; - if (len < 3) { - err = -EIO; - goto end; + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + format = kmalloc(len, GFP_KERNEL); + if (format == NULL) + return -ENOMEM; + + err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); + if (err >= 0) { + if (len < 3) + err = -EIO; + else + err = snd_oxfw_stream_parse_format(format, formation); + } + + kfree(format); + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the duplicated hard-coded format, instead. + unsigned int rate; + u8 *const *formats; + int i; + + err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0); + if (err < 0) + return err; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + formats = oxfw->rx_stream_formats; + else + formats = oxfw->tx_stream_formats; + + for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) { + if (!formats[i]) + continue; + + err = snd_oxfw_stream_parse_format(formats[i], formation); + if (err < 0) + continue; + + if (formation->rate == rate) + break; + } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EIO; } - err = snd_oxfw_stream_parse_format(format, formation); -end: - kfree(format); return err; } @@ -515,7 +548,7 @@ end: * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 */ -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation) { unsigned int i, e, channels, type; @@ -600,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, unsigned int i, eid; int err; - /* get format at current sampling rate */ - err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); - if (err < 0) { - dev_err(&oxfw->unit->device, - "fail to get current stream format for isoc %s plug %d:%d\n", - (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", - pid, err); - goto end; + // get format at current sampling rate. + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get current stream format for isoc %s plug %d:%d\n", + (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", + pid, err); + goto end; + } + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the hard-coded format, instead. + buf[0] = 0x90; + buf[1] = 0x40; + buf[2] = avc_stream_rate_table[0]; + buf[3] = 0x00; + buf[4] = 0x01; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[5] = 0x08; + else + buf[5] = 0x02; + + buf[6] = 0x06; + + *len = 7; } /* parse and set stream format */ diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 241a697ce26b..98ae0e8cba87 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -21,6 +21,7 @@ #define VENDOR_TASCAM 0x00022e #define OUI_STANTON 0x001260 #define OUI_APOGEE 0x0003db +#define OUI_OXFORD 0x0030e0 #define MODEL_SATELLITE 0x00200f #define MODEL_SCS1M 0x001000 @@ -232,6 +233,11 @@ static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent if (err < 0) goto error; + if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) { + oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED | + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS; + } + err = snd_oxfw_stream_discover(oxfw); if (err < 0) goto error; @@ -330,6 +336,9 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave), OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers), + // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the + // default value of ASIC. + OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL), // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all. OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL), // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff. @@ -337,7 +346,6 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is // installed to avoid the postpone, the value of SYT field is always 0xffff. OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL), - // Miglia HarmonyAudio. Not yet identified. // // OXFW971 devices: diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index d728e451a25c..39ea9a6dde33 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -52,6 +52,11 @@ enum snd_oxfw_quirk { // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO // are ignored, thus driver should transfer packets with timestamp. SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20, + // Miglia Harmony Audio does not support AV/C Stream Format Information command. + SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40, + // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number + // of accumulated payload quadlets including the packet. + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80, }; /* This is an arbitrary number for convinience. */ @@ -136,7 +141,7 @@ struct snd_oxfw_stream_formation { unsigned int pcm; unsigned int midi; }; -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation); int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 365c36fdf205..e9425213320e 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -127,15 +127,41 @@ static int i915_component_master_match(struct device *dev, int subcomponent, /* check whether Intel graphics is present and reachable */ static int i915_gfx_present(struct pci_dev *hdac_pci) { + /* List of known platforms with no i915 support. */ + static const struct pci_device_id denylist[] = { + /* CNL */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 }, + /* LKF */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 }, + {} + }; struct pci_dev *display_dev = NULL; if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only())) return false; for_each_pci_dev(display_dev) { - if (display_dev->vendor == PCI_VENDOR_ID_INTEL && - (display_dev->class >> 16) == PCI_BASE_CLASS_DISPLAY && - connectivity_check(display_dev, hdac_pci)) { + if (display_dev->vendor != PCI_VENDOR_ID_INTEL || + (display_dev->class >> 16) != PCI_BASE_CLASS_DISPLAY) + continue; + + if (pci_match_id(denylist, display_dev)) + continue; + + if (connectivity_check(display_dev, hdac_pci)) { pci_dev_put(display_dev); return true; } diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 610ea7a33cd8..b53de020309f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -567,7 +567,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) return 0; error: - dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", + dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", azx_dev->bufsize, period_bytes); return -EINVAL; } diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index b57d72ea4503..5f60658c6051 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -23,6 +23,10 @@ static int ctrl_link_mask; module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); +static ulong ctrl_addr = 0x40000000; +module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444); +MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR"); + static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx) { struct fwnode_handle *link; @@ -141,6 +145,9 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE) return AE_OK; /* keep going */ + if (adr != ctrl_addr) + return AE_OK; /* keep going */ + /* found the correct SoundWire controller */ info->handle = handle; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 1d786bd5ce3e..cd83aa864ea3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -41,12 +41,9 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro static void reset_tlv(struct snd_ac97 *ac97, const char *name, const unsigned int *tlv) { - struct snd_ctl_elem_id sid; struct snd_kcontrol *kctl; - memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, name); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kctl = snd_ctl_find_id(ac97->bus->card, &sid); + + kctl = snd_ctl_find_id_mixer(ac97->bus->card, name); if (kctl && kctl->tlv.p) kctl->tlv.p = tlv; } diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 2378a39abaeb..31e51e2df655 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -243,9 +243,7 @@ struct snd_ali { spinlock_t reg_lock; spinlock_t voice_alloc; -#ifdef CONFIG_PM_SLEEP - struct snd_ali_image *image; -#endif + struct snd_ali_image image; }; static const struct pci_device_id snd_ali_ids[] = { @@ -1824,18 +1822,13 @@ static int snd_ali_mixer(struct snd_ali *codec) return 0; } -#ifdef CONFIG_PM_SLEEP static int ali_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < chip->num_of_codecs; i++) snd_ac97_suspend(chip->ac97[i]); @@ -1872,13 +1865,9 @@ static int ali_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; - struct snd_ali_image *im; + struct snd_ali_image *im = &chip->image; int i, j; - im = chip->image; - if (!im) - return 0; - spin_lock_irq(&chip->reg_lock); for (i = 0; i < ALI_CHANNELS; i++) { @@ -1908,11 +1897,7 @@ static int ali_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); -#define ALI_PM_OPS &ali_pm -#else -#define ALI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); static void snd_ali_free(struct snd_card *card) { @@ -2112,13 +2097,6 @@ static int snd_ali_create(struct snd_card *card, return err; } -#ifdef CONFIG_PM_SLEEP - codec->image = devm_kmalloc(&pci->dev, sizeof(*codec->image), - GFP_KERNEL); - if (!codec->image) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif - snd_ali_enable_address_interrupt(codec); codec->hw_initialized = 1; return 0; @@ -2181,7 +2159,7 @@ static struct pci_driver ali5451_driver = { .id_table = snd_ali_ids, .probe = snd_ali_probe, .driver = { - .pm = ALI_PM_OPS, + .pm = &ali_pm, }, }; diff --git a/sound/pci/als300.c b/sound/pci/als300.c index c70aff060120..c7c481203ef8 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -654,7 +654,6 @@ static int snd_als300_create(struct snd_card *card, return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_als300_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -677,11 +676,7 @@ static int snd_als300_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); -#define SND_ALS300_PM_OPS &snd_als300_pm -#else -#define SND_ALS300_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); static int snd_als300_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) @@ -739,7 +734,7 @@ static struct pci_driver als300_driver = { .id_table = snd_als300_ids, .probe = snd_als300_probe, .driver = { - .pm = SND_ALS300_PM_OPS, + .pm = &snd_als300_pm, }, }; diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index f33aeb692a11..022473594c73 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -936,7 +936,6 @@ static int snd_card_als4000_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static int snd_als4000_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -968,18 +967,14 @@ static int snd_als4000_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); -#define SND_ALS4000_PM_OPS &snd_als4000_pm -#else -#define SND_ALS4000_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); static struct pci_driver als4000_driver = { .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .driver = { - .pm = SND_ALS4000_PM_OPS, + .pm = &snd_als4000_pm, }, }; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 43d01f1847ed..df2fef726d60 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -520,7 +520,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -530,7 +529,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1454,7 +1452,6 @@ static int snd_atiixp_mixer_new(struct atiixp *chip, int clock, } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1499,12 +1496,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1634,7 +1626,7 @@ static struct pci_driver atiixp_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8864c4c3c7e1..eb569539f322 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -496,7 +496,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_atiixp_aclink_down(struct atiixp_modem *chip) { // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ @@ -506,7 +505,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip) ATI_REG_CMD_POWERDOWN); return 0; } -#endif /* * auto-detection of codecs @@ -1094,7 +1092,6 @@ static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1128,11 +1125,7 @@ static int snd_atiixp_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); -#define SND_ATIIXP_PM_OPS &snd_atiixp_pm -#else -#define SND_ATIIXP_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); /* * proc interface for register dump @@ -1258,7 +1251,7 @@ static struct pci_driver atiixp_modem_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .driver = { - .pm = SND_ATIIXP_PM_OPS, + .pm = &snd_atiixp_pm, }, }; diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h index b5c5a71c0ac3..3a3de56b9b07 100644 --- a/sound/pci/aw2/aw2-saa7146.h +++ b/sound/pci/aw2/aw2-saa7146.h @@ -19,11 +19,12 @@ #define NUM_STREAM_CAPTURE_ANA 0 -typedef void (*snd_aw2_saa7146_it_cb) (void *); +struct snd_pcm_substream; +typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *); struct snd_aw2_saa7146_cb_param { snd_aw2_saa7146_it_cb p_it_callback; - void *p_callback_param; + struct snd_pcm_substream *p_callback_param; }; /* definition of the chip-specific record */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 431f0026b507..84989c291cd7 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -295,7 +295,6 @@ struct snd_azf3328 { * CONFIG_PM register storage below, but that's slightly difficult. */ u16 shadow_reg_ctrl_6AH; -#ifdef CONFIG_PM_SLEEP /* register value containers for power management * Note: not always full I/O range preserved (similar to Win driver!) */ u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4]; @@ -303,7 +302,6 @@ struct snd_azf3328 { u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4]; u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4]; u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4]; -#endif }; static const struct pci_device_id snd_azf3328_ids[] = { @@ -2517,7 +2515,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static inline void snd_azf3328_suspend_regs(const struct snd_azf3328 *chip, unsigned long io_addr, unsigned count, u32 *saved_regs) @@ -2633,18 +2630,14 @@ snd_azf3328_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); -#define SND_AZF3328_PM_OPS &snd_azf3328_pm -#else -#define SND_AZF3328_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); static struct pci_driver azf3328_driver = { .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .driver = { - .pm = SND_AZF3328_PM_OPS, + .pm = &snd_azf3328_pm, }, }; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 08e34b184780..36014501f7ed 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -486,10 +486,8 @@ struct cmipci { spinlock_t reg_lock; -#ifdef CONFIG_PM_SLEEP unsigned int saved_regs[0x20]; unsigned char saved_mixers[0x20]; -#endif }; @@ -3260,7 +3258,6 @@ static int snd_cmipci_probe(struct pci_dev *pci, return err; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -3324,18 +3321,14 @@ static int snd_cmipci_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); -#define SND_CMIPCI_PM_OPS &snd_cmipci_pm -#else -#define SND_CMIPCI_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); static struct pci_driver cmipci_driver = { .name = KBUILD_MODNAME, .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .driver = { - .pm = SND_CMIPCI_PM_OPS, + .pm = &snd_cmipci_pm, }, }; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 0c9cadf7b3b8..0cc86e73cc62 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -470,10 +470,7 @@ struct cs4281 { struct gameport *gameport; -#ifdef CONFIG_PM_SLEEP u32 suspend_regs[SUSPEND_REGISTERS]; -#endif - }; static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); @@ -1897,8 +1894,6 @@ static int snd_cs4281_probe(struct pci_dev *pci, /* * Power Management */ -#ifdef CONFIG_PM_SLEEP - static const int saved_regs[SUSPEND_REGISTERS] = { BA0_JSCTL, BA0_GPIOR, @@ -1987,18 +1982,14 @@ static int cs4281_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); -#define CS4281_PM_OPS &cs4281_pm -#else -#define CS4281_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); static struct pci_driver cs4281_driver = { .name = KBUILD_MODNAME, .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .driver = { - .pm = CS4281_PM_OPS, + .pm = &cs4281_pm, }, }; diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index d074727c3e21..397900929aa6 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -292,7 +292,7 @@ static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) return 0; } -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr) +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr) { int err; struct amixer_mgr *amixer_mgr; @@ -321,8 +321,9 @@ error: return err; } -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) +int amixer_mgr_destroy(void *ptr) { + struct amixer_mgr *amixer_mgr = ptr; rsc_mgr_uninit(&amixer_mgr->mgr); kfree(amixer_mgr); return 0; @@ -446,7 +447,7 @@ static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) return 0; } -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr) +int sum_mgr_create(struct hw *hw, void **rsum_mgr) { int err; struct sum_mgr *sum_mgr; @@ -475,8 +476,9 @@ error: return err; } -int sum_mgr_destroy(struct sum_mgr *sum_mgr) +int sum_mgr_destroy(void *ptr) { + struct sum_mgr *sum_mgr = ptr; rsc_mgr_uninit(&sum_mgr->mgr); kfree(sum_mgr); return 0; diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h index 4498e6139d0e..8fc017da6bda 100644 --- a/sound/pci/ctxfi/ctamixer.h +++ b/sound/pci/ctxfi/ctamixer.h @@ -43,8 +43,8 @@ struct sum_mgr { }; /* Constructor and destructor of daio resource manager */ -int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr); -int sum_mgr_destroy(struct sum_mgr *sum_mgr); +int sum_mgr_create(struct hw *hw, void **ptr); +int sum_mgr_destroy(void *ptr); /* Define the descriptor of a amixer resource */ struct amixer_rsc_ops; @@ -89,7 +89,7 @@ struct amixer_mgr { }; /* Constructor and destructor of amixer resource manager */ -int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr); -int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr); +int amixer_mgr_create(struct hw *hw, void **ramixer_mgr); +int amixer_mgr_destroy(void *amixer_mgr); #endif /* CTAMIXER_H */ diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index fbdb8a3d5b8e..2a3e9d8ba7db 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -105,23 +105,20 @@ static struct { .public_name = "Mixer"} }; -typedef int (*create_t)(struct hw *, void **); -typedef int (*destroy_t)(void *); - static struct { int (*create)(struct hw *hw, void **rmgr); int (*destroy)(void *mgr); } rsc_mgr_funcs[NUM_RSCTYP] = { - [SRC] = { .create = (create_t)src_mgr_create, - .destroy = (destroy_t)src_mgr_destroy }, - [SRCIMP] = { .create = (create_t)srcimp_mgr_create, - .destroy = (destroy_t)srcimp_mgr_destroy }, - [AMIXER] = { .create = (create_t)amixer_mgr_create, - .destroy = (destroy_t)amixer_mgr_destroy }, - [SUM] = { .create = (create_t)sum_mgr_create, - .destroy = (destroy_t)sum_mgr_destroy }, - [DAIO] = { .create = (create_t)daio_mgr_create, - .destroy = (destroy_t)daio_mgr_destroy } + [SRC] = { .create = src_mgr_create, + .destroy = src_mgr_destroy }, + [SRCIMP] = { .create = srcimp_mgr_create, + .destroy = srcimp_mgr_destroy }, + [AMIXER] = { .create = amixer_mgr_create, + .destroy = amixer_mgr_destroy }, + [SUM] = { .create = sum_mgr_create, + .destroy = sum_mgr_destroy }, + [DAIO] = { .create = daio_mgr_create, + .destroy = daio_mgr_destroy } }; static int diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 7fc720046ce2..83aaf9441ef3 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -684,7 +684,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr) return 0; } -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr) +int daio_mgr_create(struct hw *hw, void **rdaio_mgr) { int err, i; struct daio_mgr *daio_mgr; @@ -738,8 +738,9 @@ error1: return err; } -int daio_mgr_destroy(struct daio_mgr *daio_mgr) +int daio_mgr_destroy(void *ptr) { + struct daio_mgr *daio_mgr = ptr; unsigned long flags; /* free daio input mapper list */ diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h index bd6310f48013..15147fe5f74a 100644 --- a/sound/pci/ctxfi/ctdaio.h +++ b/sound/pci/ctxfi/ctdaio.h @@ -115,7 +115,7 @@ struct daio_mgr { }; /* Constructor and destructor of daio resource manager */ -int daio_mgr_create(struct hw *hw, struct daio_mgr **rdaio_mgr); -int daio_mgr_destroy(struct daio_mgr *daio_mgr); +int daio_mgr_create(struct hw *hw, void **ptr); +int daio_mgr_destroy(void *ptr); #endif /* CTDAIO_H */ diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 4a94b4708a77..159bd4008069 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -540,7 +540,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr) return 0; } -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr) +int src_mgr_create(struct hw *hw, void **rsrc_mgr) { int err, i; struct src_mgr *src_mgr; @@ -580,8 +580,9 @@ error1: return err; } -int src_mgr_destroy(struct src_mgr *src_mgr) +int src_mgr_destroy(void *ptr) { + struct src_mgr *src_mgr = ptr; rsc_mgr_uninit(&src_mgr->mgr); kfree(src_mgr); @@ -821,7 +822,7 @@ static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry) return err; } -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrcimp_mgr) +int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr) { int err; struct srcimp_mgr *srcimp_mgr; @@ -866,8 +867,9 @@ error1: return err; } -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr) +int srcimp_mgr_destroy(void *ptr) { + struct srcimp_mgr *srcimp_mgr = ptr; unsigned long flags; /* free src input mapper list */ diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h index 1124daf50c9b..e6366cc6a7ae 100644 --- a/sound/pci/ctxfi/ctsrc.h +++ b/sound/pci/ctxfi/ctsrc.h @@ -139,10 +139,10 @@ struct srcimp_mgr { }; /* Constructor and destructor of SRC resource manager */ -int src_mgr_create(struct hw *hw, struct src_mgr **rsrc_mgr); -int src_mgr_destroy(struct src_mgr *src_mgr); +int src_mgr_create(struct hw *hw, void **ptr); +int src_mgr_destroy(void *ptr); /* Constructor and destructor of SRCIMP resource manager */ -int srcimp_mgr_create(struct hw *hw, struct srcimp_mgr **rsrc_mgr); -int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr); +int srcimp_mgr_create(struct hw *hw, void **ptr); +int srcimp_mgr_destroy(void *ptr); #endif /* CTSRC_H */ diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index c70c3ac4e99a..7484de255a3e 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -34,7 +34,6 @@ static int get_firmware(const struct firmware **fw_entry, int err; char name[30]; -#ifdef CONFIG_PM_SLEEP if (chip->fw_cache[fw_index]) { dev_dbg(chip->card->dev, "firmware requested: %s is cached\n", @@ -42,7 +41,6 @@ static int get_firmware(const struct firmware **fw_entry, *fw_entry = chip->fw_cache[fw_index]; return 0; } -#endif dev_dbg(chip->card->dev, "firmware requested: %s\n", card_fw[fw_index].data); @@ -51,10 +49,8 @@ static int get_firmware(const struct firmware **fw_entry, if (err < 0) dev_err(chip->card->dev, "get_firmware(): Firmware not available (%d)\n", err); -#ifdef CONFIG_PM_SLEEP else chip->fw_cache[fw_index] = *fw_entry; -#endif return err; } @@ -63,18 +59,13 @@ static int get_firmware(const struct firmware **fw_entry, static void free_firmware(const struct firmware *fw_entry, struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); -#else - release_firmware(fw_entry); -#endif } static void free_firmware_cache(struct echoaudio *chip) { -#ifdef CONFIG_PM_SLEEP int i; for (i = 0; i < 8 ; i++) @@ -82,8 +73,6 @@ static void free_firmware_cache(struct echoaudio *chip) release_firmware(chip->fw_cache[i]); dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - -#endif } @@ -2146,8 +2135,6 @@ static int snd_echo_probe(struct pci_dev *pci, } -#if defined(CONFIG_PM_SLEEP) - static int snd_echo_suspend(struct device *dev) { struct echoaudio *chip = dev_get_drvdata(dev); @@ -2237,11 +2224,7 @@ static int snd_echo_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); -#define SND_ECHO_PM_OPS &snd_echo_pm -#else -#define SND_ECHO_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); /****************************************************************************** Everything starts and ends here @@ -2253,7 +2236,7 @@ static struct pci_driver echo_driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .driver = { - .pm = SND_ECHO_PM_OPS, + .pm = &snd_echo_pm, }, }; diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index d51de3e4edfa..511f2fcc0fb9 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -422,9 +422,7 @@ struct echoaudio { u32 __iomem *dsp_registers; /* DSP's register base */ u32 active_mask; /* Chs. active mask or * punks out */ -#ifdef CONFIG_PM_SLEEP const struct firmware *fw_cache[8]; /* Cached firmwares */ -#endif #ifdef ECHOCARD_HAS_MIDI u16 mtc_state; /* State for MIDI input parsing state machine */ diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index cc3c79387194..18b4d4b4d38d 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -274,7 +274,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->digital_mode == DIGITAL_MODE_ADAT)) return -EINVAL; - clock = 0; control_reg = le32_to_cpu(chip->comm_page->control_register); control_reg &= E3G_CLOCK_CLEAR_MASK; diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 89210b2c7342..18928b905939 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1968,7 +1968,6 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); } -#ifdef CONFIG_PM_SLEEP static int snd_ensoniq_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2007,11 +2006,7 @@ static int snd_ensoniq_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); -#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm -#else -#define SND_ENSONIQ_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); static int snd_ensoniq_create(struct snd_card *card, struct pci_dev *pci) @@ -2380,7 +2375,7 @@ static struct pci_driver ens137x_driver = { .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .driver = { - .pm = SND_ENSONIQ_PM_OPS, + .pm = &snd_ensoniq_pm, }, }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index ec598ba1a883..018a8d53ca53 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -216,9 +216,7 @@ struct es1938 { #ifdef SUPPORT_JOYSTICK struct gameport *gameport; #endif -#ifdef CONFIG_PM_SLEEP unsigned char saved_regs[SAVED_REG_SIZE]; -#endif }; static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id); @@ -1395,7 +1393,6 @@ static void snd_es1938_chip_init(struct es1938 *chip) outb(0, SLDM_REG(chip, DMACLEAR)); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -1461,11 +1458,7 @@ static int es1938_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); -#define ES1938_PM_OPS &es1938_pm -#else -#define ES1938_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); #ifdef SUPPORT_JOYSTICK static int snd_es1938_create_gameport(struct es1938 *chip) @@ -1787,7 +1780,7 @@ static struct pci_driver es1938_driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .driver = { - .pm = ES1938_PM_OPS, + .pm = &es1938_pm, }, }; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 4bc0f53c223b..c6c018b40c69 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -473,9 +473,7 @@ struct esschan { /* linked list */ struct list_head list; -#ifdef CONFIG_PM_SLEEP u16 wc_map[4]; -#endif }; struct es1968 { @@ -526,9 +524,7 @@ struct es1968 { struct list_head substream_list; spinlock_t substream_lock; -#ifdef CONFIG_PM_SLEEP u16 apu_map[NR_APUS][NR_APU_REGS]; -#endif #ifdef SUPPORT_JOYSTICK struct gameport *gameport; @@ -689,9 +685,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat { if (snd_BUG_ON(channel >= NR_APUS)) return; -#ifdef CONFIG_PM_SLEEP chip->apu_map[channel][reg] = data; -#endif reg |= (channel << 4); apu_index_set(chip, reg); apu_data_set(chip, data); @@ -976,9 +970,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es /* set the wavecache control reg */ wave_set_register(chip, es->apu[channel] << 3, tmpval); -#ifdef CONFIG_PM_SLEEP es->wc_map[channel] = tmpval; -#endif } @@ -2356,7 +2348,6 @@ static void snd_es1968_start_irq(struct es1968 *chip) outw(w, chip->io_port + ESM_PORT_HOST_IRQ); } -#ifdef CONFIG_PM_SLEEP /* * PM support */ @@ -2418,11 +2409,7 @@ static int es1968_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); -#define ES1968_PM_OPS &es1968_pm -#else -#define ES1968_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); #ifdef SUPPORT_JOYSTICK #define JOYSTICK_ADDR 0x200 @@ -2852,7 +2839,7 @@ static struct pci_driver es1968_driver = { .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .driver = { - .pm = ES1968_PM_OPS, + .pm = &es1968_pm, }, }; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 62b3cb126c6d..7f4834c2d5e6 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -222,9 +222,7 @@ struct fm801 { struct snd_tea575x tea; #endif -#ifdef CONFIG_PM_SLEEP u16 saved_regs[0x20]; -#endif }; /* @@ -1339,7 +1337,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci, return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id)); } -#ifdef CONFIG_PM_SLEEP static const unsigned char saved_regs[] = { FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, @@ -1396,18 +1393,14 @@ static int snd_fm801_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); -#define SND_FM801_PM_OPS &snd_fm801_pm -#else -#define SND_FM801_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); static struct pci_driver fm801_driver = { .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .driver = { - .pm = SND_FM801_PM_OPS, + .pm = &snd_fm801_pm, }, }; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 8e0ff70fb610..f806636242ee 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -116,6 +116,9 @@ config SND_HDA_CS_DSP_CONTROLS tristate select FW_CS_DSP +config SND_HDA_SCODEC_COMPONENT + tristate + config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C @@ -162,6 +165,7 @@ config SND_HDA_SCODEC_CS35L56_I2C select SND_HDA_SCODEC_CS35L56 select SND_HDA_CIRRUS_SCODEC select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L56 amplifier support with I2C control. @@ -177,6 +181,7 @@ config SND_HDA_SCODEC_CS35L56_SPI select SND_HDA_SCODEC_CS35L56 select SND_HDA_CIRRUS_SCODEC select SND_HDA_CS_DSP_CONTROLS + select SND_SOC_CS_AMP_LIB help Say Y or M here to include CS35L56 amplifier support with SPI control. @@ -201,6 +206,7 @@ config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC select SND_HDA_GENERIC_LEDS + select SND_HDA_SCODEC_COMPONENT help Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 793e296c3f64..13e04e1f65de 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -37,6 +37,7 @@ snd-hda-scodec-cs35l56-objs := cs35l56_hda.o snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o +snd-hda-scodec-component-objs := hda_component.o snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o # common driver @@ -67,6 +68,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o +obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o # this must be the last entry after codec drivers; diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c index e436d4dab317..72ec872afb8d 100644 --- a/sound/pci/hda/cs35l41_hda_property.c +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -51,19 +51,30 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, + { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, + { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 }, { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, @@ -210,6 +221,7 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde struct spi_device *spi; bool dsd_found; int ret; + int i; for (cfg = cs35l41_config_table; cfg->ssid; cfg++) { if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id)) @@ -295,16 +307,6 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde cs35l41->index = id == 0x40 ? 0 : 1; } - if (cfg->num_amps == 3) - /* 3 amps means a center channel, so no duplicate channels */ - cs35l41->channel_index = 0; - else - /* - * if 4 amps, there are duplicate channels, so they need different indexes - * if 2 amps, no duplicate channels, channel_index would be 0 - */ - cs35l41->channel_index = cs35l41->index / 2; - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", cs35l41->index, GPIOD_OUT_LOW, "cs35l41-reset"); @@ -312,6 +314,11 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde hw_cfg->spk_pos = cfg->channel[cs35l41->index]; + cs35l41->channel_index = 0; + for (i = 0; i < cs35l41->index; i++) + if (cfg->channel[i] == hw_cfg->spk_pos) + cs35l41->channel_index++; + if (cfg->boost_type == INTERNAL) { hw_cfg->bst_type = CS35L41_INT_BOOST; hw_cfg->bst_ind = cfg->boost_ind_nanohenry; @@ -336,6 +343,42 @@ static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physde } /* + * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has + * its own speaker id. + */ +static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* If _DSD exists for this laptop, we cannot support it through here */ + if (acpi_dev_has_props(cs35l41->dacpi)) + return -ENOENT; + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + if (cs35l41->index == 0) + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1); + else + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->valid = true; + + hw_cfg->bst_type = CS35L41_INT_BOOST; + hw_cfg->bst_ind = 1000; + hw_cfg->bst_ipk = 4100; + hw_cfg->bst_cap = 24; + hw_cfg->gpio1.func = CS35L41_NOT_USED; + hw_cfg->gpio1.valid = true; + + return 0; +} + +/* * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. * And devices created by serial-multi-instantiate don't have their device struct * pointing to the correct fwnode, so acpi_dev must be used here. @@ -392,19 +435,34 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "103C8A2E", generic_dsd_config }, { "CSC3551", "103C8A30", generic_dsd_config }, { "CSC3551", "103C8A31", generic_dsd_config }, + { "CSC3551", "103C8A6E", generic_dsd_config }, { "CSC3551", "103C8BB3", generic_dsd_config }, { "CSC3551", "103C8BB4", generic_dsd_config }, + { "CSC3551", "103C8BDD", generic_dsd_config }, + { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BDF", generic_dsd_config }, { "CSC3551", "103C8BE0", generic_dsd_config }, { "CSC3551", "103C8BE1", generic_dsd_config }, { "CSC3551", "103C8BE2", generic_dsd_config }, - { "CSC3551", "103C8BE9", generic_dsd_config }, - { "CSC3551", "103C8BDD", generic_dsd_config }, - { "CSC3551", "103C8BDE", generic_dsd_config }, { "CSC3551", "103C8BE3", generic_dsd_config }, { "CSC3551", "103C8BE5", generic_dsd_config }, { "CSC3551", "103C8BE6", generic_dsd_config }, + { "CSC3551", "103C8BE7", generic_dsd_config }, + { "CSC3551", "103C8BE8", generic_dsd_config }, + { "CSC3551", "103C8BE9", generic_dsd_config }, { "CSC3551", "103C8B3A", generic_dsd_config }, + { "CSC3551", "103C8C15", generic_dsd_config }, + { "CSC3551", "103C8C16", generic_dsd_config }, + { "CSC3551", "103C8C17", generic_dsd_config }, + { "CSC3551", "103C8C4F", generic_dsd_config }, + { "CSC3551", "103C8C50", generic_dsd_config }, + { "CSC3551", "103C8C51", generic_dsd_config }, + { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid }, + { "CSC3551", "103C8CDD", generic_dsd_config }, + { "CSC3551", "103C8CDE", generic_dsd_config }, { "CSC3551", "104312AF", generic_dsd_config }, { "CSC3551", "10431433", generic_dsd_config }, { "CSC3551", "10431463", generic_dsd_config }, diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 75a14ba54fcd..41974b3897a7 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -14,6 +14,7 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/cs-amp-lib.h> #include <sound/hda_codec.h> #include <sound/tlv.h> #include "cirrus_scodec.h" @@ -458,13 +459,15 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, if (preloaded_fw_ver) { snprintf(base_name, sizeof(base_name), - "cirrus/cs35l56-%02x%s-%06x-dsp1-misc", + "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc", + cs35l56->base.type, cs35l56->base.rev, cs35l56->base.secured ? "-s" : "", preloaded_fw_ver & 0xffffff); } else { snprintf(base_name, sizeof(base_name), - "cirrus/cs35l56-%02x%s-dsp1-misc", + "cirrus/cs35l%02x-%02x%s-dsp1-misc", + cs35l56->base.type, cs35l56->base.rev, cs35l56->base.secured ? "-s" : ""); } @@ -547,6 +550,22 @@ static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56) hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info); } +static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) +{ + int ret; + + if (!cs35l56->base.cal_data_valid || cs35l56->base.secured) + return; + + ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp, + &cs35l56_calibration_controls, + &cs35l56->base.cal_data); + if (ret < 0) + dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret); + else + dev_info(cs35l56->base.dev, "Calibration applied\n"); +} + static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) { const struct firmware *coeff_firmware = NULL; @@ -618,12 +637,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (coeff_filename) dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); - if (!firmware_missing) { - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); - if (ret) - goto err_powered_up; - } else if (wmfw_firmware || coeff_firmware) { - /* If we downloaded firmware, reset the device and wait for it to boot */ + /* If we downloaded firmware, reset the device and wait for it to boot */ + if (firmware_missing && (wmfw_firmware || coeff_firmware)) { cs35l56_system_reset(&cs35l56->base, false); regcache_mark_dirty(cs35l56->base.regmap); ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); @@ -646,6 +661,11 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (ret) dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); + cs35l56_hda_apply_calibration(cs35l56); + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + if (ret) + cs_dsp_stop(&cs35l56->cs_dsp); + err_powered_up: if (!cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); @@ -834,9 +854,10 @@ static int cs35l56_hda_system_resume(struct device *dev) return 0; } -static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) +static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) { u32 values[HDA_MAX_COMPONENTS]; + char hid_string[8]; struct acpi_device *adev; const char *property, *sub; size_t nval; @@ -847,7 +868,8 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) * the serial-multi-instantiate driver, so lookup the node by HID */ if (!ACPI_COMPANION(cs35l56->base.dev)) { - adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1); + snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid); + adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1); if (!adev) { dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n", dev_name(cs35l56->base.dev)); @@ -935,14 +957,14 @@ err: return ret; } -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) { int ret; mutex_init(&cs35l56->base.irq_lock); dev_set_drvdata(cs35l56->base.dev, cs35l56); - ret = cs35l56_hda_read_acpi(cs35l56, id); + ret = cs35l56_hda_read_acpi(cs35l56, hid, id); if (ret) goto err; @@ -953,6 +975,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) goto err; } + cs35l56->base.cal_index = cs35l56->index; + cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; @@ -990,6 +1014,10 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) if (ret) goto err; + ret = cs35l56_get_calibration(&cs35l56->base); + if (ret) + goto err; + ret = cs_dsp_halo_init(&cs35l56->cs_dsp); if (ret) { dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); @@ -1064,10 +1092,11 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = { EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56); MODULE_DESCRIPTION("CS35L56 HDA Driver"); +MODULE_IMPORT_NS(FW_CS_DSP); MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC); MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h index 6e5bc5397db5..464e4aa63cd1 100644 --- a/sound/pci/hda/cs35l56_hda.h +++ b/sound/pci/hda/cs35l56_hda.h @@ -42,7 +42,7 @@ struct cs35l56_hda { extern const struct dev_pm_ops cs35l56_hda_pm_ops; -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id); +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id); void cs35l56_hda_remove(struct device *dev); #endif /*__CS35L56_HDA_H__*/ diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c index a9ef6d86de83..13beee807308 100644 --- a/sound/pci/hda/cs35l56_hda_i2c.c +++ b/sound/pci/hda/cs35l56_hda_i2c.c @@ -13,6 +13,7 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) { + const struct i2c_device_id *id = i2c_client_get_device_id(clt); struct cs35l56_hda *cs35l56; int ret; @@ -33,7 +34,7 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) return ret; } - ret = cs35l56_hda_common_probe(cs35l56, clt->addr); + ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr); if (ret) return ret; ret = cs35l56_irq_request(&cs35l56->base, clt->irq); @@ -49,7 +50,9 @@ static void cs35l56_hda_i2c_remove(struct i2c_client *clt) } static const struct i2c_device_id cs35l56_hda_i2c_id[] = { - { "cs35l56-hda", 0 }, + { "cs35l54-hda", 0x3554 }, + { "cs35l56-hda", 0x3556 }, + { "cs35l57-hda", 0x3557 }, {} }; diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c index 080426de9083..a3b2fa76663d 100644 --- a/sound/pci/hda/cs35l56_hda_spi.c +++ b/sound/pci/hda/cs35l56_hda_spi.c @@ -13,6 +13,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) { + const struct spi_device_id *id = spi_get_device_id(spi); struct cs35l56_hda *cs35l56; int ret; @@ -33,7 +34,7 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) return ret; } - ret = cs35l56_hda_common_probe(cs35l56, spi_get_chipselect(spi, 0)); + ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0)); if (ret) return ret; ret = cs35l56_irq_request(&cs35l56->base, spi->irq); @@ -49,7 +50,9 @@ static void cs35l56_hda_spi_remove(struct spi_device *spi) } static const struct spi_device_id cs35l56_hda_spi_id[] = { - { "cs35l56-hda", 0 }, + { "cs35l54-hda", 0x3554 }, + { "cs35l56-hda", 0x3556 }, + { "cs35l57-hda", 0x3557 }, {} }; diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e63621bcb214..e51d47572557 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -231,7 +231,6 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) codec->beep = beep; INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); - mutex_init(&beep->mutex); input_dev = input_allocate_device(); if (!input_dev) { diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index db76e3ddba65..923ea862446a 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -27,7 +27,6 @@ struct hda_beep { unsigned int playing:1; unsigned int keep_power_at_enable:1; /* set by driver */ struct work_struct beep_work; /* scheduled task for beep event */ - struct mutex mutex; void (*power_hook)(struct hda_beep *beep, bool on); }; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 12f02cdc9659..2cac337f5263 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3313,7 +3313,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) list_for_each_entry(cpcm, &codec->pcm_list_head, list) { int stream; - for (stream = 0; stream < 2; stream++) { + for_each_pcm_streams(stream) { struct hda_pcm_stream *info = &cpcm->stream[stream]; if (!info->substreams) diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c new file mode 100644 index 000000000000..cd299d7d84ba --- /dev/null +++ b/sound/pci/hda/hda_component.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD audio Component Binding Interface + * + * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/acpi.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/hda_codec.h> +#include "hda_component.h" +#include "hda_local.h" + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps, + acpi_handle handle, u32 event, void *data) +{ + int i; + + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].acpi_notify) + comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event, + comps[i].dev); + } +} +EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT); + +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, int num_comps, + acpi_notify_handler handler, void *data) +{ + bool support_notifications = false; + struct acpi_device *adev; + int ret; + int i; + + adev = comps[0].adev; + if (!acpi_device_handle(adev)) + return 0; + + for (i = 0; i < num_comps; i++) + support_notifications = support_notifications || + comps[i].acpi_notifications_supported; + + if (support_notifications) { + ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + handler, data); + if (ret < 0) { + codec_warn(cdc, "Failed to install notify handler: %d\n", ret); + return 0; + } + + codec_dbg(cdc, "Notify handler installed\n"); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); + +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler) +{ + struct acpi_device *adev; + int ret; + + adev = comps[0].adev; + if (!acpi_device_handle(adev)) + return; + + ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler); + if (ret < 0) + codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action) +{ + int i; + + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].pre_playback_hook) + comps[i].pre_playback_hook(comps[i].dev, action); + } + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].playback_hook) + comps[i].playback_hook(comps[i].dev, action); + } + for (i = 0; i < num_comps; i++) { + if (comps[i].dev && comps[i].post_playback_hook) + comps[i].post_playback_hook(comps[i].dev, action); + } +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT); + +struct hda_scodec_match { + const char *bus; + const char *hid; + const char *match_str; + int index; +}; + +/* match the device name in a slightly relaxed manner */ +static int hda_comp_match_dev_name(struct device *dev, void *data) +{ + struct hda_scodec_match *p = data; + const char *d = dev_name(dev); + int n = strlen(p->bus); + char tmp[32]; + + /* check the bus name */ + if (strncmp(d, p->bus, n)) + return 0; + /* skip the bus number */ + if (isdigit(d[n])) + n++; + /* the rest must be exact matching */ + snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index); + return !strcmp(d + n, tmp); +} + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component *comps, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops) +{ + struct device *dev = hda_codec_dev(cdc); + struct component_match *match = NULL; + struct hda_scodec_match *sm; + int ret, i; + + for (i = 0; i < count; i++) { + sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); + if (!sm) + return -ENOMEM; + + sm->bus = bus; + sm->hid = hid; + sm->match_str = match_str; + sm->index = i; + comps[i].codec = cdc; + component_match_add(dev, &match, hda_comp_match_dev_name, sm); + } + + ret = component_master_add_with_match(dev, ops, match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT); + +void hda_component_manager_free(struct hda_codec *cdc, + const struct component_master_ops *ops) +{ + struct device *dev = hda_codec_dev(cdc); + + component_master_del(dev, ops); +} +EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT); + +MODULE_DESCRIPTION("HD Audio component binding library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h index bbd6f0ed16c1..c80a66691b5d 100644 --- a/sound/pci/hda/hda_component.h +++ b/sound/pci/hda/hda_component.h @@ -6,8 +6,12 @@ * Cirrus Logic International Semiconductor Ltd. */ +#ifndef __HDA_COMPONENT_H__ +#define __HDA_COMPONENT_H__ + #include <linux/acpi.h> #include <linux/component.h> +#include <sound/hda_codec.h> #define HDA_MAX_COMPONENTS 4 #define HDA_MAX_NAME_SIZE 50 @@ -23,3 +27,64 @@ struct hda_component { void (*playback_hook)(struct device *dev, int action); void (*post_playback_hook)(struct device *dev, int action); }; + +#ifdef CONFIG_ACPI +void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps, + acpi_handle handle, u32 event, void *data); +int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, int num_comps, + acpi_notify_handler handler, void *data); +void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler); +#else +static inline void hda_component_acpi_device_notify(struct hda_component *comps, + int num_comps, + acpi_handle handle, + u32 event, + void *data) +{ +} + +static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + int num_comps, + acpi_notify_handler handler, + void *data) + +{ + return 0; +} + +static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, + struct hda_component *comps, + acpi_notify_handler handler) +{ +} +#endif /* ifdef CONFIG_ACPI */ + +void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, + int action); + +int hda_component_manager_init(struct hda_codec *cdc, + struct hda_component *comps, int count, + const char *bus, const char *hid, + const char *match_str, + const struct component_master_ops *ops); + +void hda_component_manager_free(struct hda_codec *cdc, + const struct component_master_ops *ops); + +static inline int hda_component_manager_bind(struct hda_codec *cdc, + struct hda_component *comps) +{ + return component_bind_all(hda_codec_dev(cdc), comps); +} + +static inline void hda_component_manager_unbind(struct hda_codec *cdc, + struct hda_component *comps) +{ + component_unbind_all(hda_codec_dev(cdc), comps); +} + +#endif /* ifndef __HDA_COMPONENT_H__ */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index efe98f6f19a3..206306a0eb82 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -24,6 +24,7 @@ #include <sound/core.h> #include <sound/initval.h> +#include <sound/pcm_params.h> #include "hda_controller.h" #include "hda_local.h" @@ -108,6 +109,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); + struct hdac_stream *hdas = azx_stream(azx_dev); int ret = 0; trace_azx_pcm_hw_params(chip, azx_dev); @@ -117,9 +119,15 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, goto unlock; } - azx_dev->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; + /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ + hdas->bufsize = params_buffer_bytes(hw_params); + hdas->period_bytes = params_period_bytes(hw_params); + hdas->format_val = 0; + hdas->no_period_wakeup = + (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + if (snd_hdac_stream_setup_periods(hdas) < 0) + ret = -ENOMEM; unlock: dsp_unlock(azx_dev); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a1facdb98d9a..b6cd13b1775d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -133,7 +133,6 @@ struct alc_spec { u8 alc_mute_keycode_map[1]; /* component binding */ - struct component_match *match; struct hda_component comps[HDA_MAX_COMPONENTS]; }; @@ -6696,6 +6695,60 @@ static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec, } } +static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const struct coef_fw coefs[] = { + WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023), + WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03), + WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a), + WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014), + WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15), + WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489), + WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0), + WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000), + WRITE_COEF(0x6e, 0x1005), { } + }; + + static const struct hda_pintbl pincfgs[] = { + { 0x12, 0xb7a60130 }, /* Internal microphone*/ + { 0x14, 0x90170150 }, /* B&O soundbar speakers */ + { 0x17, 0x90170153 }, /* Side speakers */ + { 0x19, 0x03a11040 }, /* Headset microphone */ + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + + /* Fixes volume control problem for side speakers */ + alc295_fixup_disable_dac3(codec, fix, action); + + /* Fixes no sound from headset speaker */ + snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0); + + /* Auto-enable headset mic when plugged */ + snd_hda_jack_set_gating_jack(codec, 0x19, 0x21); + + /* Headset mic volume enhancement */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, coefs); + break; + case HDA_FIXUP_ACT_BUILD: + rename_ctl(codec, "Bass Speaker Playback Volume", + "B&O-Tuned Playback Volume"); + rename_ctl(codec, "Front Playback Switch", + "B&O Soundbar Playback Switch"); + rename_ctl(codec, "Bass Speaker Playback Switch", + "Side Speaker Playback Switch"); + break; + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -6720,79 +6773,16 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } -#ifdef CONFIG_ACPI static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) { struct hda_codec *cdc = data; struct alc_spec *spec = cdc->spec; - int i; codec_info(cdc, "ACPI Notification %d\n", event); - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].acpi_notify) - spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), event, - spec->comps[i].dev); - } -} - -static int comp_bind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - bool support_notifications = false; - struct acpi_device *adev; - int ret; - int i; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return 0; - - for (i = 0; i < HDA_MAX_COMPONENTS; i++) - support_notifications = support_notifications || - spec->comps[i].acpi_notifications_supported; - - if (support_notifications) { - ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify, cdc); - if (ret < 0) { - codec_warn(cdc, "Failed to install notify handler: %d\n", ret); - return 0; - } - - codec_dbg(cdc, "Notify handler installed\n"); - } - - return 0; -} - -static void comp_unbind_acpi(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - struct acpi_device *adev; - int ret; - - adev = spec->comps[0].adev; - if (!acpi_device_handle(adev)) - return; - - ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - comp_acpi_device_notify); - if (ret < 0) - codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); + hda_component_acpi_device_notify(spec->comps, ARRAY_SIZE(spec->comps), + handle, event, data); } -#else -static int comp_bind_acpi(struct device *dev) -{ - return 0; -} - -static void comp_unbind_acpi(struct device *dev) -{ -} -#endif static int comp_bind(struct device *dev) { @@ -6800,11 +6790,13 @@ static int comp_bind(struct device *dev) struct alc_spec *spec = cdc->spec; int ret; - ret = component_bind_all(dev, spec->comps); + ret = hda_component_manager_bind(cdc, spec->comps); if (ret) return ret; - return comp_bind_acpi(dev); + return hda_component_manager_bind_acpi_notifications(cdc, + spec->comps, ARRAY_SIZE(spec->comps), + comp_acpi_device_notify, cdc); } static void comp_unbind(struct device *dev) @@ -6812,8 +6804,8 @@ static void comp_unbind(struct device *dev) struct hda_codec *cdc = dev_to_hda_codec(dev); struct alc_spec *spec = cdc->spec; - comp_unbind_acpi(dev); - component_unbind_all(dev, spec->comps); + hda_component_manager_unbind_acpi_notifications(cdc, spec->comps, comp_acpi_device_notify); + hda_component_manager_unbind(cdc, spec->comps); } static const struct component_master_ops comp_master_ops = { @@ -6825,177 +6817,78 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_ struct snd_pcm_substream *sub, int action) { struct alc_spec *spec = cdc->spec; - int i; - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].pre_playback_hook) - spec->comps[i].pre_playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].playback_hook) - spec->comps[i].playback_hook(spec->comps[i].dev, action); - } - for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev && spec->comps[i].post_playback_hook) - spec->comps[i].post_playback_hook(spec->comps[i].dev, action); - } + hda_component_manager_playback_hook(spec->comps, ARRAY_SIZE(spec->comps), action); } -struct scodec_dev_name { - const char *bus; - const char *hid; - int index; -}; - -/* match the device name in a slightly relaxed manner */ -static int comp_match_cs35l41_dev_name(struct device *dev, void *data) +static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, const char *match_str, int count) { - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00-cs35l41-hda.%d", p->hid, p->index); - return !strcmp(d + n, tmp); -} - -static int comp_match_tas2781_dev_name(struct device *dev, - void *data) -{ - struct scodec_dev_name *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), "-%s:00", p->hid); - - return !strcmp(d + n, tmp); -} - -static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, - const char *hid, int count) -{ - struct device *dev = hda_codec_dev(cdc); struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; - int ret, i; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - for (i = 0; i < count; i++) { - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = i; - spec->comps[i].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_cs35l41_dev_name, rec); - } - ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); - if (ret) - codec_err(cdc, "Fail to register component aggregator %d\n", ret); - else - spec->gen.pcm_playback_hook = comp_generic_playback_hook; - break; - case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); - break; - } -} - -static void tas2781_generic_fixup(struct hda_codec *cdc, int action, - const char *bus, const char *hid) -{ - struct device *dev = hda_codec_dev(cdc); - struct alc_spec *spec = cdc->spec; - struct scodec_dev_name *rec; int ret; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); - if (!rec) - return; - rec->bus = bus; - rec->hid = hid; - rec->index = 0; - spec->comps[0].codec = cdc; - component_match_add(dev, &spec->match, - comp_match_tas2781_dev_name, rec); - ret = component_master_add_with_match(dev, &comp_master_ops, - spec->match); + ret = hda_component_manager_init(cdc, spec->comps, count, bus, hid, + match_str, &comp_master_ops); if (ret) - codec_err(cdc, - "Fail to register component aggregator %d\n", - ret); - else - spec->gen.pcm_playback_hook = - comp_generic_playback_hook; + return; + + spec->gen.pcm_playback_hook = comp_generic_playback_hook; break; case HDA_FIXUP_ACT_FREE: - component_master_del(dev, &comp_master_ops); + hda_component_manager_free(cdc, &comp_master_ops); break; } } static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 4); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 2); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(codec, action, "spi", "CSC3551", 4); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); } static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0100", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); } static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2); + comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); +} + +static void cs35l56_fixup_spi_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "spi", "CSC3556", "-%s:00-cs35l56-hda.%d", 4); } static void tas2781_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781"); + comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); } static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - tas2781_generic_fixup(cdc, action, "i2c", "INT8866"); + comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); } /* for alc295_fixup_hp_top_speakers */ @@ -7297,6 +7190,7 @@ enum { ALC280_FIXUP_HP_9480M, ALC245_FIXUP_HP_X360_AMP, ALC285_FIXUP_HP_SPECTRE_X360_EB1, + ALC285_FIXUP_HP_ENVY_X360, ALC288_FIXUP_DELL_HEADSET_MODE, ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, ALC288_FIXUP_DELL_XPS_13, @@ -7478,6 +7372,7 @@ enum { ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, ALC294_FIXUP_CS35L41_I2C_2, + ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9273,6 +9168,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_spectre_x360_eb1 }, + [ALC285_FIXUP_HP_ENVY_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_envy_x360, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, + }, [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_ideapad_s740_coef, @@ -9655,6 +9556,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_two, }, + [ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l56_fixup_spi_four, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -9861,6 +9768,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), @@ -9947,9 +9855,22 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), @@ -9959,8 +9880,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9988,11 +9911,35 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC245_FIXUP_CS35L56_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), @@ -10006,6 +9953,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8cde, "HP Spectre", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), @@ -10127,6 +10076,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), @@ -10559,6 +10509,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"}, {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"}, {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"}, + {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"}, {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"}, {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"}, {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"}, @@ -12674,6 +12625,7 @@ MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); +MODULE_IMPORT_NS(SND_HDA_SCODEC_COMPONENT); static struct hda_codec_driver realtek_driver = { .id = snd_hda_id_realtek, diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 1bfb00102a77..4475cea8e9f7 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -111,9 +111,7 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct acpi_device *adev; - struct device *physdev; LIST_HEAD(resources); - const char *sub; int ret; adev = acpi_dev_get_first_match_dev(hid, NULL, -1); @@ -129,18 +127,8 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); - /* No side-effect to the playback even if subsystem_id is NULL*/ - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) - sub = NULL; - - p->acpi_subsystem_id = sub; - - put_device(physdev); - return 0; err: @@ -160,11 +148,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) pm_runtime_get_sync(dev); mutex_lock(&tas_hda->priv->codec_lock); tasdevice_tuning_switch(tas_hda->priv, 0); + tas_hda->priv->playback_started = true; mutex_unlock(&tas_hda->priv->codec_lock); break; case HDA_GEN_PCM_ACT_CLOSE: mutex_lock(&tas_hda->priv->codec_lock); tasdevice_tuning_switch(tas_hda->priv, 1); + tas_hda->priv->playback_started = false; mutex_unlock(&tas_hda->priv->codec_lock); pm_runtime_mark_last_busy(dev); @@ -666,6 +656,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tasdevice_save_calibration(tas_priv); tasdevice_tuning_switch(tas_hda->priv, 0); + tas_hda->priv->playback_started = true; out: mutex_unlock(&tas_hda->priv->codec_lock); @@ -805,11 +796,8 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) pm_runtime_use_autosuspend(tas_hda->dev); pm_runtime_mark_last_busy(tas_hda->dev); pm_runtime_set_active(tas_hda->dev); - pm_runtime_get_noresume(tas_hda->dev); pm_runtime_enable(tas_hda->dev); - pm_runtime_put_autosuspend(tas_hda->dev); - tas2781_reset(tas_hda->priv); ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops); @@ -832,23 +820,19 @@ static void tas2781_hda_i2c_remove(struct i2c_client *clt) static int tas2781_runtime_suspend(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int i; dev_dbg(tas_hda->dev, "Runtime Suspend\n"); mutex_lock(&tas_hda->priv->codec_lock); + /* The driver powers up the amplifiers at module load time. + * Stop the playback if it's unused. + */ if (tas_hda->priv->playback_started) { tasdevice_tuning_switch(tas_hda->priv, 1); tas_hda->priv->playback_started = false; } - for (i = 0; i < tas_hda->priv->ndev; i++) { - tas_hda->priv->tasdevice[i].cur_book = -1; - tas_hda->priv->tasdevice[i].cur_prog = -1; - tas_hda->priv->tasdevice[i].cur_conf = -1; - } - mutex_unlock(&tas_hda->priv->codec_lock); return 0; @@ -877,16 +861,16 @@ static int tas2781_runtime_resume(struct device *dev) static int tas2781_system_suspend(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int ret; dev_dbg(tas_hda->priv->dev, "System Suspend\n"); - ret = pm_runtime_force_suspend(dev); - if (ret) - return ret; + mutex_lock(&tas_hda->priv->codec_lock); /* Shutdown chip before system suspend */ - tasdevice_tuning_switch(tas_hda->priv, 1); + if (tas_hda->priv->playback_started) + tasdevice_tuning_switch(tas_hda->priv, 1); + + mutex_unlock(&tas_hda->priv->codec_lock); /* * Reset GPIO may be shared, so cannot reset here. @@ -898,13 +882,9 @@ static int tas2781_system_suspend(struct device *dev) static int tas2781_system_resume(struct device *dev) { struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int i, ret; - - dev_info(tas_hda->priv->dev, "System Resume\n"); + int i; - ret = pm_runtime_force_resume(dev); - if (ret) - return ret; + dev_dbg(tas_hda->priv->dev, "System Resume\n"); mutex_lock(&tas_hda->priv->codec_lock); @@ -920,6 +900,10 @@ static int tas2781_system_resume(struct device *dev) * calibrated data inside algo. */ tasdevice_apply_calibration(tas_hda->priv); + + if (tas_hda->priv->playback_started) + tasdevice_tuning_switch(tas_hda->priv, 0); + mutex_unlock(&tas_hda->priv->codec_lock); return 0; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index ae285c0a629c..dae3e15ba534 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2555,7 +2555,6 @@ static void snd_intel8x0_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2628,11 +2627,7 @@ static int intel8x0_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); -#define INTEL8X0_PM_OPS &intel8x0_pm -#else -#define INTEL8X0_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ @@ -3200,7 +3195,7 @@ static struct pci_driver intel8x0_driver = { .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .driver = { - .pm = INTEL8X0_PM_OPS, + .pm = &intel8x0_pm, }, }; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 653ecca78238..3d6f5b3cc73e 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -965,7 +965,6 @@ static void snd_intel8x0m_free(struct snd_card *card) free_irq(chip->irq, chip); } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1006,11 +1005,7 @@ static int intel8x0m_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); -#define INTEL8X0M_PM_OPS &intel8x0m_pm -#else -#define INTEL8X0M_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); static void snd_intel8x0m_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) @@ -1236,7 +1231,7 @@ static struct pci_driver intel8x0m_driver = { .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .driver = { - .pm = INTEL8X0M_PM_OPS, + .pm = &intel8x0m_pm, }, }; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 305cbd24a391..f4d211970d7e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -769,9 +769,7 @@ struct snd_m3 { unsigned int in_suspend; -#ifdef CONFIG_PM_SLEEP u16 *suspend_mem; -#endif const struct firmware *assp_kernel_image; const struct firmware *assp_minisrc_image; @@ -2354,9 +2352,7 @@ static void snd_m3_free(struct snd_card *card) outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */ } -#ifdef CONFIG_PM_SLEEP vfree(chip->suspend_mem); -#endif release_firmware(chip->assp_kernel_image); release_firmware(chip->assp_minisrc_image); } @@ -2365,7 +2361,6 @@ static void snd_m3_free(struct snd_card *card) /* * APM support */ -#ifdef CONFIG_PM_SLEEP static int m3_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2439,11 +2434,7 @@ static int m3_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); -#define M3_PM_OPS &m3_pm -#else -#define M3_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); #ifdef CONFIG_SND_MAESTRO3_INPUT static int snd_m3_input_register(struct snd_m3 *chip) @@ -2587,14 +2578,14 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, chip->irq = pci->irq; card->sync_irq = chip->irq; -#ifdef CONFIG_PM_SLEEP - chip->suspend_mem = - vmalloc(array_size(sizeof(u16), - REV_B_CODE_MEMORY_LENGTH + - REV_B_DATA_MEMORY_LENGTH)); - if (chip->suspend_mem == NULL) - dev_warn(card->dev, "can't allocate apm buffer\n"); -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + chip->suspend_mem = + vmalloc(array_size(sizeof(u16), + REV_B_CODE_MEMORY_LENGTH + + REV_B_DATA_MEMORY_LENGTH)); + if (!chip->suspend_mem) + dev_warn(card->dev, "can't allocate apm buffer\n"); + } err = snd_m3_mixer(chip); if (err < 0) @@ -2706,7 +2697,7 @@ static struct pci_driver m3_driver = { .id_table = snd_m3_ids, .probe = snd_m3_probe, .driver = { - .pm = M3_PM_OPS, + .pm = &m3_pm, }, }; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 34f90829e656..11ba7d4eac2a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1356,7 +1356,6 @@ snd_nm256_peek_for_sig(struct nm256 *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * APM event handler, so the card is properly reinitialized after a power * event. @@ -1400,11 +1399,7 @@ static int nm256_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); -#define NM256_PM_OPS &nm256_pm -#else -#define NM256_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); static void snd_nm256_free(struct snd_card *card) { @@ -1660,7 +1655,7 @@ static struct pci_driver nm256_driver = { .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .driver = { - .pm = NM256_PM_OPS, + .pm = &nm256_pm, }, }; diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9dee0345f22c..7e80686fb41a 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -448,9 +448,7 @@ struct snd_riptide { unsigned long received_irqs; unsigned long handled_irqs; -#ifdef CONFIG_PM_SLEEP int in_suspend; -#endif }; struct sgd { /* scatter gather desriptor */ @@ -1142,7 +1140,6 @@ static irqreturn_t riptide_handleirq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP static int riptide_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1166,11 +1163,7 @@ static int riptide_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); -#define RIPTIDE_PM_OPS &riptide_pm -#else -#define RIPTIDE_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) { @@ -2135,7 +2128,7 @@ static struct pci_driver driver = { .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .driver = { - .pm = RIPTIDE_PM_OPS, + .pm = &riptide_pm, }, }; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 6b5ffb18197b..d50ad25574ad 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -220,12 +220,10 @@ struct rme96 { u8 rev; /* card revision number */ -#ifdef CONFIG_PM_SLEEP u32 playback_pointer; u32 capture_pointer; void *playback_suspend_buffer; void *capture_suspend_buffer; -#endif struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -1543,10 +1541,8 @@ snd_rme96_free(struct rme96 *rme96) rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); } -#ifdef CONFIG_PM_SLEEP vfree(rme96->playback_suspend_buffer); vfree(rme96->capture_suspend_buffer); -#endif } static void @@ -2329,8 +2325,6 @@ snd_rme96_create_switches(struct snd_card *card, * Card initialisation */ -#ifdef CONFIG_PM_SLEEP - static int rme96_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -2392,11 +2386,7 @@ static int rme96_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); -#define RME96_PM_OPS &rme96_pm -#else -#define RME96_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(rme96_pm, rme96_suspend, rme96_resume); static void snd_rme96_card_free(struct snd_card *card) { @@ -2432,14 +2422,14 @@ __snd_rme96_probe(struct pci_dev *pci, if (err) return err; -#ifdef CONFIG_PM_SLEEP - rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->playback_suspend_buffer) - return -ENOMEM; - rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); - if (!rme96->capture_suspend_buffer) - return -ENOMEM; -#endif + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->playback_suspend_buffer) + return -ENOMEM; + rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->capture_suspend_buffer) + return -ENOMEM; + } strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { @@ -2483,7 +2473,7 @@ static struct pci_driver rme96_driver = { .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .driver = { - .pm = RME96_PM_OPS, + .pm = &rme96_pm, }, }; diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index fabe393607f8..53206beb2cb5 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -90,11 +90,7 @@ struct voice { * we're not doing power management, we still need to allocate a page * for the silence buffer. */ -#ifdef CONFIG_PM_SLEEP #define SIS_SUSPEND_PAGES 4 -#else -#define SIS_SUSPEND_PAGES 1 -#endif struct sis7019 { unsigned long ioport; @@ -1152,7 +1148,6 @@ static int sis_chip_init(struct sis7019 *sis) return 0; } -#ifdef CONFIG_PM_SLEEP static int sis_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1231,11 +1226,7 @@ error: return -EIO; } -static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); -#define SIS_PM_OPS &sis_pm -#else -#define SIS_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); static int sis_alloc_suspend(struct sis7019 *sis) { @@ -1397,7 +1388,7 @@ static struct pci_driver sis7019_driver = { .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .driver = { - .pm = SIS_PM_OPS, + .pm = &sis_pm, }, }; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index d8666ff7bdfa..89838b4fb118 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -347,13 +347,11 @@ struct via82xx { unsigned char old_legacy; unsigned char old_legacy_cfg; -#ifdef CONFIG_PM_SLEEP unsigned char legacy_saved; unsigned char legacy_cfg_saved; unsigned char spdif_ctrl_saved; unsigned char capture_src_saved[2]; unsigned int mpu_port_saved; -#endif unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */ @@ -2031,9 +2029,7 @@ static int snd_via686_init_misc(struct via82xx *chip) if (mpu_port >= 0x200) { /* force MIDI */ mpu_port &= 0xfffc; pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01); -#ifdef CONFIG_PM_SLEEP chip->mpu_port_saved = mpu_port; -#endif } else { mpu_port = pci_resource_start(chip->pci, 2); } @@ -2085,10 +2081,8 @@ static int snd_via686_init_misc(struct via82xx *chip) snd_via686_create_gameport(chip, &legacy); -#ifdef CONFIG_PM_SLEEP chip->legacy_saved = legacy; chip->legacy_cfg_saved = legacy_cfg; -#endif return 0; } @@ -2234,7 +2228,6 @@ static int snd_via82xx_chip_init(struct via82xx *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -2287,11 +2280,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -2576,7 +2565,7 @@ static struct pci_driver via82xx_driver = { .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ca7f024bf8ec..a0a49b8d1511 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1008,7 +1008,6 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) return 0; } -#ifdef CONFIG_PM_SLEEP /* * power management */ @@ -1042,11 +1041,7 @@ static int snd_via82xx_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); -#define SND_VIA82XX_PM_OPS &snd_via82xx_pm -#else -#define SND_VIA82XX_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); static void snd_via82xx_free(struct snd_card *card) { @@ -1168,7 +1163,7 @@ static struct pci_driver via82xx_modem_driver = { .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .driver = { - .pm = SND_VIA82XX_PM_OPS, + .pm = &snd_via82xx_pm, }, }; diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 273688c05317..fa74635cee08 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -132,9 +132,26 @@ config SND_SOC_AMD_RPL_ACP6x Say m if you have such a device. If unsure select "N". +config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + tristate + select SOUNDWIRE_AMD if SND_SOC_AMD_SOUNDWIRE != n + select SND_AMD_SOUNDWIRE_ACPI if ACPI + +config SND_SOC_AMD_SOUNDWIRE + tristate "Support for SoundWire based AMD platforms" + default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + depends on ACPI && SOUNDWIRE + depends on !(SOUNDWIRE=m && SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE=y) + help + This adds support for SoundWire for AMD platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + config SND_SOC_AMD_PS tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support" select SND_AMD_ACP_CONFIG + select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE depends on X86 && PCI && ACPI help This option enables Audio Coprocessor i.e ACP v6.3 support on diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 82e1cf864a40..ebbe49c2bbff 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ -obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ +obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/ obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/ obj-$(CONFIG_SND_SOC_AMD_PS) += ps/ diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 84c963241dc5..30590a23ad63 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -116,3 +116,10 @@ config SND_SOC_AMD_SOF_MACH This option enables SOF sound card support for ACP audio. endif # SND_SOC_AMD_ACP_COMMON + +config SND_AMD_SOUNDWIRE_ACPI + tristate + depends on ACPI + help + This options enables ACPI helper functions for SoundWire + interface for AMD platforms. diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index ff5f7893b81e..1fd581a2aa33 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -10,6 +10,7 @@ snd-acp-i2s-objs := acp-i2s.o snd-acp-pdm-objs := acp-pdm.o snd-acp-legacy-common-objs := acp-legacy-common.o snd-acp-pci-objs := acp-pci.o +snd-amd-sdw-acpi-objs := amd-sdw-acpi.o #platform specific driver snd-acp-renoir-objs := acp-renoir.o @@ -33,6 +34,7 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o +obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 504d1b8c4cbb..665a6ea0a2a8 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -828,8 +828,8 @@ static const struct snd_soc_ops acp_card_maxim_ops = { }; SND_SOC_DAILINK_DEF(max98388, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"), - COMP_CODEC("i2c-ADS8388:01", "max98388-aif1"))); + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI), + COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI))); static const struct snd_kcontrol_new max98388_controls[] = { SOC_DAPM_PIN_SWITCH("Left Spk"), @@ -1280,7 +1280,7 @@ static const struct snd_soc_ops acp_8821_ops = { SND_SOC_DAILINK_DEF(nau8821, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00", - "nau8821-hifi"))); + NAU8821_CODEC_DAI))); /* Declare DMIC codec components */ SND_SOC_DAILINK_DEF(dmic_codec, diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 20b94814a046..fc59ea34e687 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -28,7 +28,6 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -38,7 +37,6 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_rt1019_data = { @@ -49,7 +47,6 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = { .amp_codec_id = RT1019, .dmic_codec_id = DMIC, .platform = RENOIR, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_max_data = { @@ -60,7 +57,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, .platform = RENOIR, - .tdm_mode = false, }; static struct acp_card_drvdata sof_nau8825_data = { @@ -72,7 +68,6 @@ static struct acp_card_drvdata sof_nau8825_data = { .dmic_codec_id = DMIC, .platform = REMBRANDT, .soc_mclk = true, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { @@ -84,20 +79,15 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .dmic_codec_id = DMIC, .platform = REMBRANDT, .soc_mclk = true, - .tdm_mode = false, }; static struct acp_card_drvdata sof_nau8821_max98388_data = { .hs_cpu_id = I2S_SP, .amp_cpu_id = I2S_HS, .bt_cpu_id = I2S_BT, - .dmic_cpu_id = NONE, .hs_codec_id = NAU8821, .amp_codec_id = MAX98388, - .bt_codec_id = NONE, - .dmic_codec_id = NONE, .soc_mclk = true, - .tdm_mode = false, }; static int acp_sof_probe(struct platform_device *pdev) @@ -126,16 +116,14 @@ static int acp_sof_probe(struct platform_device *pdev) if (dmi_id && dmi_id->driver_data) acp_card_drvdata->tdm_mode = dmi_id->driver_data; - acp_sofdsp_dai_links_create(card); + ret = acp_sofdsp_dai_links_create(card); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n"); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, - "devm_snd_soc_register_card(%s) failed: %d\n", - card->name, ret); - return ret; - } - + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register card(%s)\n", card->name); return 0; } @@ -182,7 +170,7 @@ static struct platform_driver acp_asoc_audio = { module_platform_driver(acp_asoc_audio); MODULE_IMPORT_NS(SND_SOC_AMD_MACH); -MODULE_DESCRIPTION("ACP chrome SOF audio support"); +MODULE_DESCRIPTION("ACP SOF Machine Driver"); MODULE_ALIAS("platform:rt5682-rt1019"); MODULE_ALIAS("platform:rt5682-max"); MODULE_ALIAS("platform:rt5682s-max"); diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c new file mode 100644 index 000000000000..babd841d3296 --- /dev/null +++ b/sound/soc/amd/acp/amd-sdw-acpi.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> + +/* + * SDW AMD ACPI scan helper function + */ + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/fwnode.h> +#include <linux/module.h> +#include <linux/soundwire/sdw_amd.h> +#include <linux/string.h> + +int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info) +{ + struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); + u32 sdw_bitmap = 0; + u8 count = 0; + int ret; + + if (!adev) + return -EINVAL; + + /* Found controller, find links supported */ + ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev), + "mipi-sdw-manager-list", &sdw_bitmap, 1); + if (ret) { + dev_err(&adev->dev, + "Failed to read mipi-sdw-manager-list: %d\n", ret); + return -EINVAL; + } + count = hweight32(sdw_bitmap); + /* Check count is within bounds */ + if (count > info->count) { + dev_err(&adev->dev, "Manager count %d exceeds max %d\n", + count, info->count); + return -EINVAL; + } + + if (!count) { + dev_dbg(&adev->dev, "No SoundWire Managers detected\n"); + return -EINVAL; + } + dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count); + info->link_mask = sdw_bitmap; + return 0; +} +EXPORT_SYMBOL_NS(amd_sdw_scan_controller, SND_AMD_SOUNDWIRE_ACPI); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("AMD SoundWire ACPI helpers"); diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile index f2a5eaf2fa4d..b3c254886fd9 100644 --- a/sound/soc/amd/ps/Makefile +++ b/sound/soc/amd/ps/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0+ +# SPDX-License-Identifier: GPL-2.0-only # Pink Sardine platform Support snd-pci-ps-objs := pci-ps.o snd-ps-pdm-dma-objs := ps-pdm-dma.o diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 8b853b8d0219..39208305dd6c 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -5,12 +5,12 @@ * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved. */ +#include <linux/soundwire/sdw_amd.h> #include <sound/acp63_chip_offset_byte.h> #define ACP_DEVICE_ID 0x15E2 #define ACP63_REG_START 0x1240000 -#define ACP63_REG_END 0x1250200 -#define ACP63_DEVS 5 +#define ACP63_REG_END 0x125C000 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP_PGFSM_CNTL_POWER_ON_MASK 1 @@ -55,32 +55,6 @@ #define ACP_DMIC_DEV 2 -/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */ -#define ACP63_PDM_MODE_DEVS 3 - -/* - * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for - * SW0 SoundWire manager instance configuration - */ -#define ACP63_SDW0_MODE_DEVS 2 - -/* - * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager - * instances configuration - */ -#define ACP63_SDW0_SDW1_MODE_DEVS 3 - -/* - * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager - * instance + ACP PDM controller configuration - */ -#define ACP63_SDW0_PDM_MODE_DEVS 4 - -/* - * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for - * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration - */ -#define ACP63_SDW0_SDW1_PDM_MODE_DEVS 5 #define ACP63_DMIC_ADDR 2 #define ACP63_SDW_ADDR 5 #define AMD_SDW_MAX_MANAGERS 2 @@ -88,17 +62,6 @@ /* time in ms for acp timeout */ #define ACP_TIMEOUT 500 -/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */ -#define ACP63_PDM_DEV_CONFIG BIT(0) - -/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */ -#define ACP63_SDW_DEV_CONFIG BIT(1) - -/* - * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire - * manager instance combination. - */ -#define ACP63_SDW_PDM_DEV_CONFIG GENMASK(1, 0) #define ACP_SDW0_STAT BIT(21) #define ACP_SDW1_STAT BIT(2) #define ACP_ERROR_IRQ BIT(29) @@ -253,38 +216,46 @@ struct sdw_dma_ring_buf_reg { * struct acp63_dev_data - acp pci driver context * @acp63_base: acp mmio base * @res: resource - * @pdev: array of child platform device node structures + * @pdm_dev: ACP PDM controller platform device + * @dmic_codec: platform device for DMIC Codec + * sdw_dma_dev: platform device for SoundWire DMA controller + * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration * @acp_lock: used to protect acp common registers - * @sdw_fw_node: SoundWire controller fw node handle - * @pdev_config: platform device configuration - * @pdev_count: platform devices count - * @pdm_dev_index: pdm platform device index - * @sdw_manager_count: SoundWire manager instance count - * @sdw0_dev_index: SoundWire Manager-0 platform device index - * @sdw1_dev_index: SoundWire Manager-1 platform device index - * @sdw_dma_dev_index: SoundWire DMA controller platform device index + * @info: SoundWire AMD information found in ACPI tables + * @sdw: SoundWire context for all SoundWire manager instances + * @machine: ACPI machines for SoundWire interface + * @is_sdw_dev: flag set to true when any SoundWire manager instances are available + * @is_pdm_dev: flag set to true when ACP PDM controller exists + * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS + * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS + * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled + * @addr: pci ioremap address + * @reg_range: ACP reigister range * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance - * @acp_reset: flag set to true when bus reset is applied across all - * the active SoundWire manager instances */ struct acp63_dev_data { void __iomem *acp63_base; struct resource *res; - struct platform_device *pdev[ACP63_DEVS]; + struct platform_device *pdm_dev; + struct platform_device *dmic_codec_dev; + struct platform_device *sdw_dma_dev; + struct platform_device *mach_dev; struct mutex acp_lock; /* protect shared registers */ - struct fwnode_handle *sdw_fw_node; - u16 pdev_config; - u16 pdev_count; - u16 pdm_dev_index; - u8 sdw_manager_count; - u16 sdw0_dev_index; - u16 sdw1_dev_index; - u16 sdw_dma_dev_index; + struct sdw_amd_acpi_info info; + /* sdw context allocated by SoundWire driver */ + struct sdw_amd_ctx *sdw; + struct snd_soc_acpi_mach *machines; + bool is_sdw_dev; + bool is_pdm_dev; + bool is_pdm_config; + bool is_sdw_config; + bool sdw_en_stat; + u32 addr; + u32 reg_range; u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; - bool acp_reset; }; int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 5927eef04170..c72d666d51bd 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD Pink Sardine ACP PCI Driver * @@ -17,6 +17,7 @@ #include <linux/pm_runtime.h> #include <linux/iopoll.h> #include <linux/soundwire/sdw_amd.h> +#include "../mach-config.h" #include "acp63.h" @@ -104,10 +105,8 @@ static irqreturn_t acp63_irq_thread(int irq, void *context) struct sdw_dma_dev_data *sdw_dma_data; struct acp63_dev_data *adata = context; u32 stream_index; - u16 pdev_index; - pdev_index = adata->sdw_dma_dev_index; - sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev); for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) { if (adata->sdw0_dma_intr_stat[stream_index]) { @@ -135,7 +134,6 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) u32 stream_id = 0; u16 irq_flag = 0; u16 sdw_dma_irq_flag = 0; - u16 pdev_index; u16 index; adata = dev_id; @@ -149,8 +147,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ext_intr_stat & ACP_SDW0_STAT) { writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); - pdev_index = adata->sdw0_dev_index; - amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev); if (amd_manager) schedule_work(&amd_manager->amd_sdw_irq_thread); irq_flag = 1; @@ -159,8 +156,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); if (ext_intr_stat1 & ACP_SDW1_STAT) { writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); - pdev_index = adata->sdw1_dev_index; - amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev); if (amd_manager) schedule_work(&amd_manager->amd_sdw_irq_thread); irq_flag = 1; @@ -176,8 +172,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) } if (ext_intr_stat & BIT(PDM_DMA_STAT)) { - pdev_index = adata->pdm_dev_index; - ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev); writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ps_pdm_data->capture_stream) snd_pcm_period_elapsed(ps_pdm_data->capture_stream); @@ -237,122 +232,165 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) return IRQ_NONE; } -static int sdw_amd_scan_controller(struct device *dev) +#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE) +static int acp_scan_sdw_devices(struct device *dev, u64 addr) { + struct acpi_device *sdw_dev; struct acp63_dev_data *acp_data; - struct fwnode_handle *link; - char name[32]; - u32 sdw_manager_bitmap; - u8 count = 0; - u32 acp_sdw_power_mode = 0; - int index; + + acp_data = dev_get_drvdata(dev); + if (!addr) + return -ENODEV; + + sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0); + if (!sdw_dev) + return -ENODEV; + + acp_data->info.handle = sdw_dev->handle; + acp_data->info.count = AMD_SDW_MAX_MANAGERS; + return amd_sdw_scan_controller(&acp_data->info); +} + +static int amd_sdw_probe(struct device *dev) +{ + struct acp63_dev_data *acp_data; + struct sdw_amd_res sdw_res; int ret; acp_data = dev_get_drvdata(dev); - /* - * Current implementation is based on MIPI DisCo 2.0 spec. - * Found controller, find links supported. - */ - ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list", - &sdw_manager_bitmap, 1); + memset(&sdw_res, 0, sizeof(sdw_res)); + sdw_res.addr = acp_data->addr; + sdw_res.reg_range = acp_data->reg_range; + sdw_res.handle = acp_data->info.handle; + sdw_res.parent = dev; + sdw_res.dev = dev; + sdw_res.acp_lock = &acp_data->acp_lock; + sdw_res.count = acp_data->info.count; + sdw_res.mmio_base = acp_data->acp63_base; + sdw_res.link_mask = acp_data->info.link_mask; + ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); + if (ret) + dev_err(dev, "error: SoundWire probe failed\n"); + return ret; +} - if (ret) { - dev_dbg(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret); - return -EINVAL; - } - count = hweight32(sdw_manager_bitmap); - /* Check count is within bounds */ - if (count > AMD_SDW_MAX_MANAGERS) { - dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS); - return -EINVAL; - } - - if (!count) { - dev_dbg(dev, "No SoundWire Managers detected\n"); - return -EINVAL; - } - dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count); - acp_data->sdw_manager_count = count; - for (index = 0; index < count; index++) { - scnprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index); - link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name); - if (!link) { - dev_err(dev, "Manager node %s not found\n", name); - return -EIO; +static int amd_sdw_exit(struct acp63_dev_data *acp_data) +{ + if (acp_data->sdw) + sdw_amd_exit(acp_data->sdw); + acp_data->sdw = NULL; + + return 0; +} + +static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) +{ + struct snd_soc_acpi_mach *mach; + const struct snd_soc_acpi_link_adr *link; + struct acp63_dev_data *acp_data = dev_get_drvdata(dev); + int ret, i; + + if (acp_data->info.count) { + ret = sdw_amd_get_slave_info(acp_data->sdw); + if (ret) { + dev_dbg(dev, "failed to read slave information\n"); + return NULL; + } + for (mach = acp_data->machines; mach; mach++) { + if (!mach->links) + break; + link = mach->links; + for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { + if (!snd_soc_acpi_sdw_link_slaves_found(dev, link, + acp_data->sdw->ids, + acp_data->sdw->num_slaves)) + break; + } + if (i == acp_data->info.count || !link->num_adr) + break; + } + if (mach && mach->link_mask) { + mach->mach_params.links = mach->links; + mach->mach_params.link_mask = mach->link_mask; + return mach; } + } + dev_dbg(dev, "No SoundWire machine driver found\n"); + return NULL; +} +#else +static int acp_scan_sdw_devices(struct device *dev, u64 addr) +{ + return 0; +} - ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode); - if (ret) - return ret; - /* - * when SoundWire configuration is selected from acp pin config, - * based on manager instances count, acp init/de-init sequence should be - * executed as part of PM ops only when Bus reset is applied for the active - * SoundWire manager instances. - */ - if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) { - acp_data->acp_reset = false; - return 0; +static int amd_sdw_probe(struct device *dev) +{ + return 0; +} + +static int amd_sdw_exit(struct acp63_dev_data *acp_data) +{ + return 0; +} + +static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) +{ + return NULL; +} +#endif + +static int acp63_machine_register(struct device *dev) +{ + struct snd_soc_acpi_mach *mach; + struct acp63_dev_data *adata = dev_get_drvdata(dev); + int size; + + if (adata->is_sdw_dev && adata->is_sdw_config) { + size = sizeof(*adata->machines); + mach = acp63_sdw_machine_select(dev); + if (mach) { + adata->mach_dev = platform_device_register_data(dev, mach->drv_name, + PLATFORM_DEVID_NONE, mach, + size); + if (IS_ERR(adata->mach_dev)) { + dev_err(dev, + "cannot register Machine device for SoundWire Interface\n"); + return PTR_ERR(adata->mach_dev); + } + } + + } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) { + adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach", + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(adata->mach_dev)) { + dev_err(dev, "cannot register amd_ps_mach device\n"); + return PTR_ERR(adata->mach_dev); } } return 0; } -static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data) +static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) { - struct acpi_device *dmic_dev; - struct acpi_device *sdw_dev; + struct acpi_device *pdm_dev; const union acpi_object *obj; + u32 config; bool is_dmic_dev = false; bool is_sdw_dev = false; int ret; - dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); - if (dmic_dev) { - /* is_dmic_dev flag will be set when ACP PDM controller device exists */ - if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type", - ACPI_TYPE_INTEGER, &obj) && - obj->integer.value == ACP_DMIC_DEV) - is_dmic_dev = true; - } - - sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0); - if (sdw_dev) { - acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev); - ret = sdw_amd_scan_controller(&pci->dev); - /* is_sdw_dev flag will be set when SoundWire Manager device exists */ - if (!ret) - is_sdw_dev = true; - } - if (!is_dmic_dev && !is_sdw_dev) - return -ENODEV; - dev_dbg(&pci->dev, "Audio Mode %d\n", config); + config = readl(acp_data->acp63_base + ACP_PIN_CONFIG); switch (config) { case ACP_CONFIG_4: case ACP_CONFIG_5: case ACP_CONFIG_10: case ACP_CONFIG_11: - if (is_dmic_dev) { - acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_PDM_MODE_DEVS; - } + acp_data->is_pdm_config = true; break; case ACP_CONFIG_2: case ACP_CONFIG_3: - if (is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; - break; - default: - return -EINVAL; - } - } + acp_data->is_sdw_config = true; break; case ACP_CONFIG_6: case ACP_CONFIG_7: @@ -360,40 +398,36 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63 case ACP_CONFIG_8: case ACP_CONFIG_13: case ACP_CONFIG_14: - if (is_dmic_dev && is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS; - break; - default: - return -EINVAL; - } - } else if (is_dmic_dev) { - acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_PDM_MODE_DEVS; - } else if (is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; - break; - default: - return -EINVAL; - } - } + acp_data->is_pdm_config = true; + acp_data->is_sdw_config = true; break; default: break; } + + if (acp_data->is_pdm_config) { + pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); + if (pdm_dev) { + /* is_dmic_dev flag will be set when ACP PDM controller device exists */ + if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type", + ACPI_TYPE_INTEGER, &obj) && + obj->integer.value == ACP_DMIC_DEV) + is_dmic_dev = true; + } + } + + if (acp_data->is_sdw_config) { + ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR); + if (!ret && acp_data->info.link_mask) + is_sdw_dev = true; + } + + acp_data->is_pdm_dev = is_dmic_dev; + acp_data->is_sdw_dev = is_sdw_dev; + if (!is_dmic_dev && !is_sdw_dev) { + dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); + return -ENODEV; + } return 0; } @@ -418,17 +452,13 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr) { - struct acp_sdw_pdata *sdw_pdata; - struct platform_device_info pdevinfo[ACP63_DEVS]; + struct platform_device_info pdevinfo; struct device *parent; - int index; int ret; parent = &pci->dev; - dev_dbg(&pci->dev, - "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config, - adata->pdev_count); - if (adata->pdev_config) { + + if (adata->is_sdw_dev || adata->is_pdm_dev) { adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); if (!adata->res) { ret = -ENOMEM; @@ -440,130 +470,57 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data memset(&pdevinfo, 0, sizeof(pdevinfo)); } - switch (adata->pdev_config) { - case ACP63_PDM_DEV_CONFIG: - adata->pdm_dev_index = 0; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", + if (adata->is_pdm_dev && adata->is_pdm_config) { + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma", 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach", - 0, NULL, 0, NULL, 0); - break; - case ACP63_SDW_DEV_CONFIG: - if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - sdw_pdata->instance = 0; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->sdw0_dev_index = 0; - adata->sdw_dma_dev_index = 1; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - sdw_pdata, sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - - sdw_pdata[0].instance = 0; - sdw_pdata[1].instance = 1; - sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; - sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->sdw0_dev_index = 0; - adata->sdw1_dev_index = 1; - adata->sdw_dma_dev_index = 2; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 1, adata->res, 1, - &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); + adata->pdm_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->pdm_dev)) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->pdm_dev); + goto de_init; } - break; - case ACP63_SDW_PDM_DEV_CONFIG: - if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - - sdw_pdata->instance = 0; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->pdm_dev_index = 0; - adata->sdw0_dev_index = 1; - adata->sdw_dma_dev_index = 2; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - sdw_pdata, sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); - } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - sdw_pdata[0].instance = 0; - sdw_pdata[1].instance = 1; - sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; - sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; - adata->pdm_dev_index = 0; - adata->sdw0_dev_index = 1; - adata->sdw1_dev_index = 2; - adata->sdw_dma_dev_index = 3; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node, - "amd_sdw_manager", 1, adata->res, 1, - &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec", + 0, NULL, 0, NULL, 0); + adata->dmic_codec_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->dmic_codec_dev)) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->dmic_codec_dev); + goto unregister_pdm_dev; } - break; - default: - dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); - return 0; } + if (adata->is_sdw_dev && adata->is_sdw_config) { + ret = amd_sdw_probe(&pci->dev); + if (ret) { + if (adata->is_pdm_dev) + goto unregister_dmic_codec_dev; + else + goto de_init; + } + memset(&pdevinfo, 0, sizeof(pdevinfo)); + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); - for (index = 0; index < adata->pdev_count; index++) { - adata->pdev[index] = platform_device_register_full(&pdevinfo[index]); - if (IS_ERR(adata->pdev[index])) { + adata->sdw_dma_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->sdw_dma_dev)) { dev_err(&pci->dev, - "cannot register %s device\n", pdevinfo[index].name); - ret = PTR_ERR(adata->pdev[index]); - goto unregister_devs; + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->sdw_dma_dev); + if (adata->is_pdm_dev) + goto unregister_dmic_codec_dev; + else + goto de_init; } } + return 0; -unregister_devs: - for (--index; index >= 0; index--) - platform_device_unregister(adata->pdev[index]); +unregister_dmic_codec_dev: + platform_device_unregister(adata->dmic_codec_dev); +unregister_pdm_dev: + platform_device_unregister(adata->pdm_dev); de_init: if (acp63_deinit(adata->acp63_base, &pci->dev)) dev_err(&pci->dev, "ACP de-init failed\n"); @@ -576,7 +533,6 @@ static int snd_acp63_probe(struct pci_dev *pci, struct acp63_dev_data *adata; u32 addr; u32 irqflags, flag; - int val; int ret; irqflags = IRQF_SHARED; @@ -618,13 +574,8 @@ static int snd_acp63_probe(struct pci_dev *pci, ret = -ENOMEM; goto release_regions; } - /* - * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init() - * will be invoked for all ACP configurations during suspend/resume callbacks. - * This flag should be set to false only when SoundWire manager power mode - * set to ClockStopMode. - */ - adata->acp_reset = true; + adata->addr = addr; + adata->reg_range = ACP63_REG_END - ACP63_REG_START; pci_set_master(pci); pci_set_drvdata(pci, adata); mutex_init(&adata->acp_lock); @@ -637,8 +588,7 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); goto de_init; } - val = readl(adata->acp63_base + ACP_PIN_CONFIG); - ret = get_acp63_device_config(val, pci, adata); + ret = get_acp63_device_config(pci, adata); /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */ if (ret) { dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret); @@ -649,6 +599,11 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP platform devices creation failed\n"); goto de_init; } + ret = acp63_machine_register(&pci->dev); + if (ret) { + dev_err(&pci->dev, "ACP machine register failed\n"); + goto de_init; + } skip_pdev_creation: device_set_wakeup_enable(&pci->dev, true); pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); @@ -667,47 +622,103 @@ disable_pci: return ret; } +static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata) +{ + u32 sdw0_en, sdw1_en; + + sdw0_en = readl(adata->acp63_base + ACP_SW0_EN); + sdw1_en = readl(adata->acp63_base + ACP_SW1_EN); + return (sdw0_en || sdw1_en); +} + +static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata) +{ + u32 val; + + val = readl(adata->acp63_base + ACP_SW0_WAKE_EN); + if (val && adata->sdw->pdev[0]) + pm_request_resume(&adata->sdw->pdev[0]->dev); + + val = readl(adata->acp63_base + ACP_SW1_WAKE_EN); + if (val && adata->sdw->pdev[1]) + pm_request_resume(&adata->sdw->pdev[1]->dev); +} + static int __maybe_unused snd_acp63_suspend(struct device *dev) { struct acp63_dev_data *adata; - int ret = 0; + int ret; adata = dev_get_drvdata(dev); - if (adata->acp_reset) { - ret = acp63_deinit(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP de-init failed\n"); + if (adata->is_sdw_dev) { + adata->sdw_en_stat = check_acp_sdw_enable_status(adata); + if (adata->sdw_en_stat) + return 0; } + ret = acp63_deinit(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + return ret; } -static int __maybe_unused snd_acp63_resume(struct device *dev) +static int __maybe_unused snd_acp63_runtime_resume(struct device *dev) { struct acp63_dev_data *adata; - int ret = 0; + int ret; adata = dev_get_drvdata(dev); - if (adata->acp_reset) { - ret = acp63_init(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP init failed\n"); + if (adata->sdw_en_stat) + return 0; + + ret = acp63_init(adata->acp63_base, dev); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; } + + if (!adata->sdw_en_stat) + handle_acp63_sdw_pme_event(adata); + return 0; +} + +static int __maybe_unused snd_acp63_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->sdw_en_stat) + return 0; + + ret = acp63_init(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP init failed\n"); + return ret; } static const struct dev_pm_ops acp63_pm_ops = { - SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_resume, NULL) + SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume) }; static void snd_acp63_remove(struct pci_dev *pci) { struct acp63_dev_data *adata; - int ret, index; + int ret; adata = pci_get_drvdata(pci); - for (index = 0; index < adata->pdev_count; index++) - platform_device_unregister(adata->pdev[index]); + if (adata->sdw) { + amd_sdw_exit(adata); + platform_device_unregister(adata->sdw_dma_dev); + } + if (adata->is_pdm_dev) { + platform_device_unregister(adata->pdm_dev); + platform_device_unregister(adata->dmic_codec_dev); + } + if (adata->mach_dev) + platform_device_unregister(adata->mach_dev); ret = acp63_deinit(adata->acp63_base, &pci->dev); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); @@ -740,4 +751,6 @@ module_pci_driver(ps_acp63_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver"); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); +MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c index 3ffbe4fdafdf..e675b8f569eb 100644 --- a/sound/soc/amd/ps/ps-mach.c +++ b/sound/soc/amd/ps/ps-mach.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * Machine driver for AMD Pink Sardine platform using DMIC * diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index d48f7c5af289..7bbacbab1095 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD ALSA SoC Pink Sardine PDM Driver * diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 9b59063798f2..66b800962f8c 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD ALSA SoC Pink Sardine SoundWire DMA Driver * diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index 18a8760443ae..8341a6e06493 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -140,7 +140,7 @@ static int snd_proto_probe(struct platform_device *pdev) dai->dai_fmt = dai_fmt; - ret = snd_soc_register_card(&snd_proto); + ret = devm_snd_soc_register_card(&pdev->dev, &snd_proto); if (ret) dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); @@ -155,11 +155,6 @@ put_codec_node: return ret; } -static void snd_proto_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&snd_proto); -} - static const struct of_device_id snd_proto_of_match[] = { { .compatible = "mikroe,mikroe-proto", }, {}, @@ -172,7 +167,6 @@ static struct platform_driver snd_proto_driver = { .of_match_table = snd_proto_of_match, }, .probe = snd_proto_probe, - .remove_new = snd_proto_remove, }; module_platform_driver(snd_proto_driver); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 59f9742e9ff4..f78ea2f86fa6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -114,6 +114,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES8328_I2C imply SND_SOC_ES7134 imply SND_SOC_ES7241 + imply SND_SOC_FRAMER imply SND_SOC_GTM601 imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA @@ -276,6 +277,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WCD9335 imply SND_SOC_WCD934X imply SND_SOC_WCD938X_SDW + imply SND_SOC_WCD939X_SDW imply SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO @@ -727,6 +729,22 @@ config SND_SOC_CROS_EC_CODEC If you say yes here you will get support for the ChromeOS Embedded Controller's Audio Codec. +config SND_SOC_CS_AMP_LIB + tristate + +config SND_SOC_CS_AMP_LIB_TEST + tristate "KUnit test for Cirrus Logic cs-amp-lib" + depends on KUNIT + default KUNIT_ALL_TESTS + select SND_SOC_CS_AMP_LIB + help + This builds KUnit tests for the Cirrus Logic common + amplifier library. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". + config SND_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C @@ -795,6 +813,7 @@ config SND_SOC_CS35L56 tristate config SND_SOC_CS35L56_SHARED + select SND_SOC_CS_AMP_LIB tristate config SND_SOC_CS35L56_I2C @@ -1101,6 +1120,20 @@ config SND_SOC_ES8328_SPI depends on SPI_MASTER select SND_SOC_ES8328 +config SND_SOC_FRAMER + tristate "Framer codec" + depends on GENERIC_FRAMER + help + Enable support for the framer codec. + The framer codec uses the generic framer infrastructure to transport + some audio data over an analog E1/T1/J1 line. + This codec allows to use some of the time slots available on the TDM + bus on which the framer is connected to transport the audio data. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-framer. + + config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' @@ -2059,6 +2092,25 @@ config SND_SOC_WCD938X_SDW The WCD9380/9385 is a audio codec IC Integrated in Qualcomm SoCs like SM8250. +config SND_SOC_WCD939X + depends on SND_SOC_WCD939X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + depends on TYPEC || !TYPEC + select SND_SOC_WCD_CLASSH + +config SND_SOC_WCD939X_SDW + tristate "WCD9390/WCD9395 Codec - SDW" + depends on TYPEC || !TYPEC + select SND_SOC_WCD939X + select SND_SOC_WCD_MBHC + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9390/9395 is a audio codec IC Integrated in + Qualcomm SoCs like SM8650. + config SND_SOC_WL1273 tristate @@ -2300,7 +2352,6 @@ config SND_SOC_WSA881X tristate "WSA881X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier. @@ -2309,7 +2360,6 @@ config SND_SOC_WSA883X tristate "WSA883X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. @@ -2318,7 +2368,6 @@ config SND_SOC_WSA884X tristate "WSA884X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D Smart Speaker Amplifier. diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f53baa2b9565..7c075539dc47 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -59,6 +59,8 @@ snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o +snd-soc-cs-amp-lib-objs := cs-amp-lib.o +snd-soc-cs-amp-lib-test-objs := cs-amp-lib-test.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o @@ -122,6 +124,7 @@ snd-soc-es8326-objs := es8326.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-framer-objs := framer-codec.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hda-objs := hdac_hda.o @@ -313,6 +316,8 @@ snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd938x-objs := wcd938x.o snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o +snd-soc-wcd939x-objs := wcd939x.o +snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -449,6 +454,8 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o @@ -512,6 +519,7 @@ obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o @@ -703,6 +711,11 @@ ifdef CONFIG_SND_SOC_WCD938X_SDW # avoid link failure by forcing sdw code built-in when needed obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o endif +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x.o +ifdef CONFIG_SND_SOC_WCD939X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o +endif obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c new file mode 100644 index 000000000000..15f991b2e16e --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// KUnit test for the Cirrus common amplifier library. +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/test.h> +#include <kunit/static_stub.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/firmware/cirrus/wmfw.h> +#include <linux/gpio/driver.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <sound/cs-amp-lib.h> + +struct cs_amp_lib_test_priv { + struct platform_device amp_pdev; + + struct cirrus_amp_efi_data *cal_blob; + struct list_head ctl_write_list; +}; + +struct cs_amp_lib_test_ctl_write_entry { + struct list_head list; + unsigned int value; + char name[16]; +}; + +struct cs_amp_lib_test_param { + int num_amps; + int amp_index; +}; + +static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + unsigned int blob_size; + + blob_size = offsetof(struct cirrus_amp_efi_data, data) + + sizeof(struct cirrus_amp_cal_data) * num_amps; + + priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob); + + priv->cal_blob->size = blob_size; + priv->cal_blob->count = num_amps; + + get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps); +} + +static u64 cs_amp_lib_test_get_target_uid(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + u64 uid; + + uid = priv->cal_blob->data[param->amp_index].calTarget[1]; + uid <<= 32; + uid |= priv->cal_blob->data[param->amp_index].calTarget[0]; + + return uid; +} + +/* Redirected get_efi_variable to simulate that the file is too short */ +static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + if (!buf) { + *size = offsetof(struct cirrus_amp_efi_data, data) - 1; + return EFI_BUFFER_TOO_SMALL; + } + + return EFI_NOT_FOUND; +} + +/* Should return -EOVERFLOW if the header is larger than the EFI data */ +static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_nohead); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the count is larger than the file */ +static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + if (!buf) { + /* + * Return a size that is shorter than required for the + * declared number of entries. + */ + *size = priv->cal_blob->size - 1; + return EFI_BUFFER_TOO_SMALL; + } + + memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1); + + return EFI_SUCCESS; +} + +/* Should return -EOVERFLOW if the entry count is larger than the EFI data */ +static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_bad_count); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the variable not found */ +static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + return EFI_NOT_FOUND; +} + +/* If EFI doesn't contain a cal data variable the result should be -ENOENT */ +static void cs_amp_lib_test_no_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_none); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate reading a cal data blob */ +static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData"; + static const efi_guid_t expected_guid = + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size); + + KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name)); + KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid)); + + if (!buf) { + *size = priv->cal_blob->size; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small"); + + memcpy(buf, priv->cal_blob, priv->cal_blob->size); + + return EFI_SUCCESS; +} + +/* Get cal data block for a given amp, matched by target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + target_uid = cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL); + KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32); + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index without checking target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index with checked target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + target_uid = cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * Get cal data block for a given amp index with checked target UID. + * The UID does not match so the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + /* Get a target UID that won't match the entry */ + target_uid = ~cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * Get cal data block for a given amp, where the cal data does not + * specify calTarget so the lookup falls back to using the index + */ +static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * If the target UID isn't present in the cal data, and there isn't an + * index to fall back do, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't present in the cal data, and the index is + * out of range, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't given, and the index is out of range, the + * result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* If neither the target UID or the index is given the result should be -ENOENT. */ +static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the UID is passed as 0 this must not match an entry with an + * unpopulated calTarget + */ +static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; + +static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name); + KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls); + + entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry); + + INIT_LIST_HEAD(&entry->list); + strscpy(entry->name, ctl_name, sizeof(entry->name)); + entry->value = val; + + list_add_tail(&entry->list, &priv->ctl_write_list); + + return 0; +} + +static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + struct cirrus_amp_cal_data data; + struct cs_dsp *dsp; + int ret; + + dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp); + dsp->dev = &priv->amp_pdev.dev; + + get_random_bytes(&data, sizeof(data)); + + /* Redirect calls to write firmware controls */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->write_cal_coeff, + cs_amp_lib_test_write_cal_coeff); + + ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff); + + KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4); + + /* Checksum control must be written last */ + entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum); + KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient); + KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr); + KUNIT_EXPECT_EQ(test, entry->value, data.calR); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status); + KUNIT_EXPECT_EQ(test, entry->value, data.calStatus); +} + +static void cs_amp_lib_test_dev_release(struct device *dev) +{ +} + +static int cs_amp_lib_test_case_init(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + INIT_LIST_HEAD(&priv->ctl_write_list); + + /* Create dummy amp driver dev */ + priv->amp_pdev.name = "cs_amp_lib_test_drv"; + priv->amp_pdev.id = -1; + priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release; + ret = platform_device_register(&priv->amp_pdev); + KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n"); + + return 0; +} + +static void cs_amp_lib_test_case_exit(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + + if (priv->amp_pdev.name) + platform_device_unregister(&priv->amp_pdev); +} + +static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = { + { .num_amps = 2, .amp_index = 0 }, + { .num_amps = 2, .amp_index = 1 }, + + { .num_amps = 3, .amp_index = 0 }, + { .num_amps = 3, .amp_index = 1 }, + { .num_amps = 3, .amp_index = 2 }, + + { .num_amps = 4, .amp_index = 0 }, + { .num_amps = 4, .amp_index = 1 }, + { .num_amps = 4, .amp_index = 2 }, + { .num_amps = 4, .amp_index = 3 }, + + { .num_amps = 5, .amp_index = 0 }, + { .num_amps = 5, .amp_index = 1 }, + { .num_amps = 5, .amp_index = 2 }, + { .num_amps = 5, .amp_index = 3 }, + { .num_amps = 5, .amp_index = 4 }, + + { .num_amps = 6, .amp_index = 0 }, + { .num_amps = 6, .amp_index = 1 }, + { .num_amps = 6, .amp_index = 2 }, + { .num_amps = 6, .amp_index = 3 }, + { .num_amps = 6, .amp_index = 4 }, + { .num_amps = 6, .amp_index = 5 }, + + { .num_amps = 8, .amp_index = 0 }, + { .num_amps = 8, .amp_index = 1 }, + { .num_amps = 8, .amp_index = 2 }, + { .num_amps = 8, .amp_index = 3 }, + { .num_amps = 8, .amp_index = 4 }, + { .num_amps = 8, .amp_index = 5 }, + { .num_amps = 8, .amp_index = 6 }, + { .num_amps = 8, .amp_index = 7 }, +}; + +static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d", + param->num_amps, param->amp_index); +} + +KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases, + cs_amp_lib_test_get_cal_param_desc); + +static struct kunit_case cs_amp_lib_test_cases[] = { + /* Tests for getting calibration data from EFI */ + KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test), + KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test), + KUNIT_CASE(cs_amp_lib_test_no_cal_data_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test, + cs_amp_lib_test_get_cal_gen_params), + + /* Tests for writing calibration data */ + KUNIT_CASE(cs_amp_lib_test_write_cal_data_test), + + { } /* terminator */ +}; + +static struct kunit_suite cs_amp_lib_test_suite = { + .name = "snd-soc-cs-amp-lib-test", + .init = cs_amp_lib_test_case_init, + .exit = cs_amp_lib_test_case_exit, + .test_cases = cs_amp_lib_test_cases, +}; + +kunit_test_suite(cs_amp_lib_test_suite); + +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); +MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c new file mode 100644 index 000000000000..01ef4db5407d --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Common code for Cirrus Logic Smart Amplifiers +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <asm/byteorder.h> +#include <kunit/static_stub.h> +#include <linux/dev_printk.h> +#include <linux/efi.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <sound/cs-amp-lib.h> + +#define CS_AMP_CAL_GUID \ + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) + +#define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibrationData" + +static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct cs_dsp_coeff_ctl *cs_ctl; + __be32 beval = cpu_to_be32(val); + int ret; + + KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); + + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { + mutex_lock(&dsp->pwr_lock); + cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); + mutex_unlock(&dsp->pwr_lock); + + if (ret < 0) { + dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); + return ret; + } + + return 0; + } + + return -ENODEV; +} + +static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + int ret; + + dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", + data->calAmbient, data->calStatus, data->calR); + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1); + if (ret) + return ret; + + return 0; +} + +/** + * cs_amp_write_cal_coeffs - Write calibration data to firmware controls. + * @dsp: Pointer to struct cs_dsp. + * @controls: Pointer to definition of firmware controls to be written. + * @data: Pointer to calibration data. + * + * Returns: 0 on success, else negative error value. + */ +int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return _cs_amp_write_cal_coeffs(dsp, controls, data); + else + return -ENODEV; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, SND_SOC_CS_AMP_LIB); + +static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + u32 attr; + + KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf); + + if (IS_ENABLED(CONFIG_EFI)) + return efi.get_variable(name, guid, &attr, size, buf); + + return EFI_NOT_FOUND; +} + +static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) +{ + struct cirrus_amp_efi_data *efi_data; + unsigned long data_size = 0; + u8 *data; + efi_status_t status; + int ret; + + /* Get real size of UEFI variable */ + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return ERR_PTR(-ENOENT); + + if (data_size < sizeof(*efi_data)) { + dev_err(dev, "EFI cal variable truncated\n"); + return ERR_PTR(-EOVERFLOW); + } + + /* Get variable contents into buffer */ + data = kmalloc(data_size, GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data); + if (status != EFI_SUCCESS) { + ret = -EINVAL; + goto err; + } + + efi_data = (struct cirrus_amp_efi_data *)data; + dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); + + if ((efi_data->count > 128) || + offsetof(struct cirrus_amp_efi_data, data[efi_data->count]) > data_size) { + dev_err(dev, "EFI cal variable truncated\n"); + ret = -EOVERFLOW; + goto err; + } + + return efi_data; + +err: + kfree(data); + dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); + + return ERR_PTR(ret); +} + +static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data) +{ + return ((u64)data->calTarget[1] << 32) | data->calTarget[0]; +} + +static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + struct cirrus_amp_efi_data *efi_data; + struct cirrus_amp_cal_data *cal = NULL; + int i, ret; + + efi_data = cs_amp_get_cal_efi_buffer(dev); + if (IS_ERR(efi_data)) + return PTR_ERR(efi_data); + + if (target_uid) { + for (i = 0; i < efi_data->count; ++i) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]); + + /* Skip entries with unpopulated silicon ID */ + if (cal_target == 0) + continue; + + if (cal_target == target_uid) { + cal = &efi_data->data[i]; + break; + } + } + } + + if (!cal && (amp_index >= 0) && (amp_index < efi_data->count)) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]); + + /* + * Treat unpopulated cal_target as a wildcard. + * If target_uid != 0 we can only get here if cal_target == 0 + * or it didn't match any cal_target value. + * If target_uid == 0 it is a wildcard. + */ + if ((cal_target == 0) || (target_uid == 0)) + cal = &efi_data->data[amp_index]; + else + dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); + } + + if (cal) { + memcpy(out_data, cal, sizeof(*out_data)); + ret = 0; + } else { + dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); + ret = -ENOENT; + } + + kfree(efi_data); + + return ret; +} + +/** + * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI. + * @dev: struct device of the caller. + * @target_uid: UID to match, or zero to ignore UID matching. + * @amp_index: Entry index to use, or -1 to prevent lookup by index. + * @out_data: struct cirrus_amp_cal_data where the entry will be copied. + * + * This function can perform 3 types of lookup: + * + * (target_uid > 0, amp_index >= 0) + * UID search with fallback to using the array index. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else, if the entry at + * [amp_index] has calTarget == 0, return that entry. Else fail. + * + * (target_uid > 0, amp_index < 0) + * UID search only. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else fail. + * + * (target_uid == 0, amp_index >= 0) + * Array index fetch only. + * Return the entry at [amp_index]. + * + * An array lookup will be skipped if amp_index exceeds the number of + * entries in the calibration array, and in this case the return will + * be -ENOENT. An out-of-range amp_index does not prevent matching by + * target_uid - it has the same effect as passing amp_index < 0. + * + * If the EFI data is too short to be a valid entry, or the entry count + * in the EFI data overflows the actual length of the data, this function + * returns -EOVERFLOW. + * + * Return: 0 if the entry was found, -ENOENT if no entry was found, + * -EOVERFLOW if the EFI file is corrupt, else other error value. + */ +int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); + else + return -ENOENT; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, SND_SOC_CS_AMP_LIB); + +static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { + .get_efi_variable = cs_amp_get_efi_variable, + .write_cal_coeff = cs_amp_write_cal_coeff, +}; + +const struct cs_amp_test_hooks * const cs_amp_test_hooks = + PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); +EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SOC_CS_AMP_LIB); + +MODULE_DESCRIPTION("Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index ab960a1c171e..14a5f86019aa 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -161,6 +161,20 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; +static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56) +{ + int ret; + + /* SoundWire UniqueId is used to index the calibration array */ + ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0); + if (ret < 0) + return ret; + + cs35l56->base.cal_index = ret & 0xf; + + return 0; +} + static void cs35l56_sdw_init(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -168,6 +182,12 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) pm_runtime_get_noresume(cs35l56->base.dev); + if (cs35l56->base.cal_index < 0) { + ret = cs35l56_sdw_set_cal_index(cs35l56); + if (ret < 0) + goto out; + } + regcache_cache_only(cs35l56->base.regmap, false); ret = cs35l56_init(cs35l56); @@ -366,7 +386,7 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row); - if (cs35l56->base.rev < 0xb0) + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0)) return cs35l56_a1_kick_divider(cs35l56, peripheral); return 0; @@ -543,6 +563,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { static const struct sdw_device_id cs35l56_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index cb4e83126b08..08cac58e3ab2 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -5,10 +5,12 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include <linux/firmware/cirrus/wmfw.h> #include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> +#include <sound/cs-amp-lib.h> #include "cs35l56.h" @@ -36,6 +38,8 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { + /* no defaults for OTP_MEM - first read populates cache */ + { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, { CS35L56_ASP1_CONTROL2, 0x18180200 }, @@ -91,6 +95,9 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_BLOCK_ENABLES2: case CS35L56_REFCLK_INPUT: case CS35L56_GLOBAL_SAMPLE_RATE: + case CS35L56_OTP_MEM_53: + case CS35L56_OTP_MEM_54: + case CS35L56_OTP_MEM_55: case CS35L56_ASP1_ENABLES1: case CS35L56_ASP1_CONTROL1: case CS35L56_ASP1_CONTROL2: @@ -629,6 +636,82 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds } EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); +struct cs35l56_pte { + u8 x; + u8 wafer_id; + u8 pte[2]; + u8 lot[3]; + u8 y; + u8 unused[3]; + u8 dvs; +} __packed; +static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0); + +static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) +{ + struct cs35l56_pte pte; + u64 unique_id; + int ret; + + ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte)); + if (ret) { + dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret); + return ret; + } + + unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16); + unique_id <<= 32; + unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) | + ((u32)pte.dvs << 24); + + dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id); + + *uid = unique_id; + + return 0; +} + +/* Firmware calibration controls */ +const struct cirrus_amp_cal_controls cs35l56_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED); + +int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) +{ + u64 silicon_uid; + int ret; + + /* Driver can't apply calibration to a secured part, so skip */ + if (cs35l56_base->secured) + return 0; + + ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); + if (ret < 0) + return ret; + + ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid, + cs35l56_base->cal_index, + &cs35l56_base->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) + return ret; + + cs35l56_base->cal_data_valid = true; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, SND_SOC_CS35L56_SHARED); + int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version) { @@ -693,13 +776,17 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) devid &= CS35L56_DEVID_MASK; switch (devid) { + case 0x35A54: case 0x35A56: + case 0x35A57: break; default: dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); return ret; } + cs35l56_base->type = devid & 0xFF; + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) { dev_err(cs35l56_base->dev, "Get Secure status failed\n"); @@ -720,8 +807,8 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) if (ret) return ret; - dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", - cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", + cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing); /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ @@ -923,3 +1010,4 @@ MODULE_DESCRIPTION("ASoC CS35L56 Shared"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 6dd0319bc843..8d2f021fb362 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -23,6 +23,7 @@ #include <linux/soundwire/sdw.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <sound/cs-amp-lib.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -802,16 +803,44 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { } }; +static int cs35l56_write_cal(struct cs35l56_private *cs35l56) +{ + int ret; + + if (cs35l56->base.secured || !cs35l56->base.cal_data_valid) + return -ENODATA; + + ret = wm_adsp_run(&cs35l56->dsp); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp, + &cs35l56_calibration_controls, + &cs35l56->base.cal_data); + + wm_adsp_stop(&cs35l56->dsp); + + if (ret == 0) + dev_info(cs35l56->base.dev, "Calibration applied\n"); + + return ret; +} + static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56) { int ret; /* Use wm_adsp to load and apply the firmware patch and coefficient files */ ret = wm_adsp_power_up(&cs35l56->dsp, true); - if (ret) + if (ret) { dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); - else - cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + return; + } + + cs35l56_write_cal(cs35l56); + + /* Always REINIT after applying patch or coefficients */ + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); } static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing) @@ -874,6 +903,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing CS35L56_FIRMWARE_MISSING); cs35l56->base.fw_patched = true; + if (cs35l56_write_cal(cs35l56) == 0) + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + err_unlock: mutex_unlock(&cs35l56->base.irq_lock); err: @@ -972,6 +1004,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENODEV; } + cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type); + if (!cs35l56->dsp.part) + return -ENOMEM; + cs35l56->component = component; wm_adsp2_component_probe(&cs35l56->dsp, component); @@ -1002,6 +1038,9 @@ static void cs35l56_component_remove(struct snd_soc_component *component) wm_adsp2_component_remove(&cs35l56->dsp, component); + kfree(cs35l56->dsp.part); + cs35l56->dsp.part = NULL; + kfree(cs35l56->dsp.fwf_name); cs35l56->dsp.fwf_name = NULL; @@ -1221,7 +1260,12 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) dsp = &cs35l56->dsp; cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); - dsp->part = "cs35l56"; + + /* + * dsp->part is filled in later as it is based on the DEVID. In a + * SoundWire system that cannot be read until enumeration has occurred + * and the device has attached. + */ dsp->fw = 12; dsp->wmfw_optional = true; @@ -1356,6 +1400,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) init_completion(&cs35l56->init_completion); mutex_init(&cs35l56->base.irq_lock); + cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; dev_set_drvdata(cs35l56->base.dev, cs35l56); @@ -1457,6 +1502,10 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (ret) return ret; + ret = cs35l56_get_calibration(&cs35l56->base); + if (ret) + return ret; + if (!cs35l56->base.reset_gpio) { dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n"); cs35l56->soft_resetting = true; @@ -1541,6 +1590,7 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { MODULE_DESCRIPTION("ASoC CS35L56 driver"); MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2d11c5125f73..60d366e53526 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2195,7 +2195,6 @@ int cs42l42_suspend(struct device *dev) /* Discharge FILT+ */ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK); - msleep(CS42L42_FILT_DISCHARGE_TIME_MS); regcache_cache_only(cs42l42->regmap, true); gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 24a598f2ed9a..901b9dbcf585 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -6,29 +6,35 @@ // Cirrus Logic International Semiconductor Ltd. #include <linux/build_bug.h> +#include <linux/completion.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/irq.h> #include <linux/jiffies.h> #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> +#include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/property.h> +#include <linux/regmap.h> +#include <linux/time.h> +#include <linux/workqueue.h> #include <sound/control.h> #include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-component.h> +#include <sound/soc-jack.h> #include <sound/soc.h> #include "cs42l43.h" static const unsigned int cs42l43_accdet_us[] = { - 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000, }; static const unsigned int cs42l43_accdet_db_ms[] = { - 0, 125, 250, 500, 750, 1000, 1250, 1500 + 0, 125, 250, 500, 750, 1000, 1250, 1500, }; static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; @@ -101,8 +107,13 @@ int cs42l43_set_jack(struct snd_soc_component *component, goto error; } - device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", - priv->buttons, ret); + ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", + ret); + goto error; + } } else { priv->buttons[0] = 70; priv->buttons[1] = 185; @@ -637,7 +648,7 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) static int cs42l43_run_type_detect(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; - int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200; unsigned int type = 0xff; unsigned long time_left; @@ -846,6 +857,9 @@ static const char * const cs42l43_jack_text[] = { "Line-In", "Microphone", "Optical", }; +static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) == + ARRAY_SIZE(cs42l43_jack_text) - 1); + SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -868,9 +882,6 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int override = ucontrol->value.integer.value[0]; - BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != - ARRAY_SIZE(cs42l43_jack_text) - 1); - if (override >= e->items) return -EINVAL; diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c index 388f95853b69..60c00c05da05 100644 --- a/sound/soc/codecs/cs42l43-sdw.c +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -9,6 +9,7 @@ #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> #include <linux/module.h> +#include <linux/soundwire/sdw.h> #include <sound/pcm.h> #include <sound/sdw.h> #include <sound/soc-component.h> diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index a97ccb512deb..860d5cda67bf 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -6,17 +6,27 @@ // Cirrus Logic International Semiconductor Ltd. #include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/find.h> #include <linux/gcd.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/jiffies.h> #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/string.h> +#include <linux/workqueue.h> #include <sound/control.h> +#include <sound/cs42l43.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-component.h> @@ -539,23 +549,22 @@ static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask, + int *slots, unsigned int nslots) { - int i; + int i = 0; + int slot; - for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { - int slot = ffs(mask) - 1; - - if (slot < 0) + for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) { + if (i == nslots) { + dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n", + mask); return; + } - slots[i] = slot; - - mask &= ~(1 << slot); + slots[i++] = slot; } - if (mask) - dev_warn(priv->dev, "Too many channels in TDM mask\n"); } static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -572,8 +581,10 @@ static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mas rx_mask = CS42L43_DEFAULT_SLOTS; } - cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); - cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); return 0; } @@ -1051,12 +1062,10 @@ static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); return ret; } @@ -1069,12 +1078,10 @@ static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = snd_soc_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = snd_soc_get_volsw(kcontrol, ucontrol); return ret; } @@ -1331,10 +1338,9 @@ static int cs42l43_enable_pll(struct cs42l43_codec *priv) dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); - while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { - div++; - freq /= 2; - } + div = fls(freq) - + fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq); + freq >>= div; if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { int i; @@ -2094,8 +2100,10 @@ static int cs42l43_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, cs42l43->regmap); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); priv->component = component; priv->constraint = cs42l43_constraint; @@ -2103,10 +2111,28 @@ static int cs42l43_component_probe(struct snd_soc_component *component) return 0; } +static void cs42l43_component_remove(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + cs42l43_set_jack(priv->component, NULL, NULL); + + cancel_delayed_work_sync(&priv->bias_sense_timeout); + cancel_delayed_work_sync(&priv->tip_sense_work); + cancel_delayed_work_sync(&priv->button_press_work); + cancel_work_sync(&priv->button_release_work); + + cancel_work_sync(&priv->hp_ilimit_work); + cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); + + priv->component = NULL; +} + static const struct snd_soc_component_driver cs42l43_component_drv = { .name = "cs42l43-codec", .probe = cs42l43_component_probe, + .remove = cs42l43_component_remove, .set_sysclk = cs42l43_set_sysclk, .set_jack = cs42l43_set_jack, @@ -2387,7 +2413,7 @@ MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); static struct platform_driver cs42l43_codec_driver = { .driver = { .name = "cs42l43-codec", - .pm = &cs42l43_codec_pm_ops, + .pm = pm_ptr(&cs42l43_codec_pm_ops), }, .probe = cs42l43_codec_probe, diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 125e36861d5d..9924c13e1eb5 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -6,19 +6,14 @@ * Cirrus Logic International Semiconductor Ltd. */ -#include <linux/clk.h> +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + #include <linux/completion.h> -#include <linux/device.h> #include <linux/mutex.h> -#include <linux/regmap.h> -#include <linux/soundwire/sdw.h> #include <linux/types.h> -#include <sound/cs42l43.h> +#include <linux/workqueue.h> #include <sound/pcm.h> -#include <sound/soc-jack.h> - -#ifndef CS42L43_ASOC_INT_H -#define CS42L43_ASOC_INT_H #define CS42L43_INTERNAL_SYSCLK 24576000 #define CS42L43_DEFAULT_SLOTS 0x3F @@ -37,6 +32,14 @@ #define CS42L43_N_BUTTONS 6 +struct clk; +struct device; + +struct snd_soc_component; +struct snd_soc_jack; + +struct cs42l43; + struct cs42l43_codec { struct device *dev; struct cs42l43 *core; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 0e5c527687a2..369c62078780 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2101,18 +2101,14 @@ static int da7213_probe(struct snd_soc_component *component) pm_runtime_put_sync(component->dev); /* Check if MCLK provided */ - da7213->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(da7213->mclk)) { - if (PTR_ERR(da7213->mclk) != -ENOENT) - return PTR_ERR(da7213->mclk); - else - da7213->mclk = NULL; - } else { + da7213->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(da7213->mclk)) + return PTR_ERR(da7213->mclk); + if (da7213->mclk) /* Do automatic PLL handling assuming fixed clock until * set_pll() has been called. This makes the codec usable * with the simple-audio-card driver. */ da7213->fixed_clk_auto_pll = true; - } /* Default infinite tone gen, start/stop by Kcontrol */ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index cbcd02ec6ba4..15289dadafea 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -31,11 +31,11 @@ struct es8326_priv { * while enabling or disabling or during an irq. */ struct mutex lock; - u8 mic1_src; - u8 mic2_src; u8 jack_pol; u8 interrupt_src; u8 interrupt_clk; + u8 hpl_vol; + u8 hpr_vol; bool jd_inverted; unsigned int sysclk; @@ -121,6 +121,72 @@ static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol, return 0; } +static int es8326_hplvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpl_vol; + + return 0; +} + +static int es8326_hplvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpl_vol != hp_vol) { + es8326->hpl_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x70, (hp_vol << 4)); + return 1; + } + + return 0; +} + +static int es8326_hprvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpr_vol; + + return 0; +} + +static int es8326_hprvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpr_vol != hp_vol) { + es8326->hpr_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x07, hp_vol); + return 1; + } + + return 0; +} + static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); @@ -151,15 +217,24 @@ static const char *const winsize[] = { static const char *const dacpol_txt[] = { "Normal", "R Invert", "L Invert", "L + R Invert" }; +static const char *const hp_spkvol_switch[] = { + "HPVOL: HPL+HPL, SPKVOL: HPL+HPL", + "HPVOL: HPL+HPR, SPKVOL: HPL+HPR", + "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR", + "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR", +}; + static const struct soc_enum dacpol = SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); static const struct soc_enum drc_winsize = SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); +static const struct soc_enum hpvol_spkvol_switch = + SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch); static const struct snd_kcontrol_new es8326_snd_controls[] = { - SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), SOC_ENUM("Playback Polarity", dacpol), SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate), SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv), @@ -182,6 +257,17 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { es8326_crosstalk1_get, es8326_crosstalk1_set), SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, es8326_crosstalk2_get, es8326_crosstalk2_set), + SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hplvol_get, es8326_hplvol_set), + SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hprvol_get, es8326_hprvol_set), + + SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv), + + SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch), }; static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { @@ -972,6 +1058,8 @@ static int es8326_resume(struct snd_soc_component *component) es8326->jack_remove_retry = 0; es8326->hp = 0; + es8326->hpl_vol = 0x03; + es8326->hpr_vol = 0x03; return 0; } @@ -1002,20 +1090,6 @@ static int es8326_probe(struct snd_soc_component *component) es8326->jd_inverted = device_property_read_bool(component->dev, "everest,jack-detect-inverted"); - ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src); - if (ret != 0) { - dev_dbg(component->dev, "mic1-src return %d", ret); - es8326->mic1_src = ES8326_ADC_AMIC; - } - dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src); - - ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src); - if (ret != 0) { - dev_dbg(component->dev, "mic2-src return %d", ret); - es8326->mic2_src = ES8326_ADC_DMIC; - } - dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src); - ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); if (ret != 0) { dev_dbg(component->dev, "jack-pol return %d", ret); @@ -1035,7 +1109,7 @@ static int es8326_probe(struct snd_soc_component *component) &es8326->interrupt_clk); if (ret != 0) { dev_dbg(component->dev, "interrupt-clk return %d", ret); - es8326->interrupt_clk = 0x45; + es8326->interrupt_clk = 0x00; } dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 4234bbb900c4..ee12caef8105 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -69,7 +69,7 @@ #define ES8326_DAC_DSM 0x4D #define ES8326_DAC_RAMPRATE 0x4E #define ES8326_DAC_VPPSCALE 0x4F -#define ES8326_DAC_VOL 0x50 +#define ES8326_DACL_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_DAC_CROSSTALK 0x55 @@ -81,6 +81,9 @@ #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C +#define ES8326_DACR_VOL 0xF4 +#define ES8326_SPKL_VOL 0xF5 +#define ES8326_SPKR_VOL 0xF6 #define ES8326_HP_MISC 0xF7 #define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9 diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c new file mode 100644 index 000000000000..e5fcde9ee308 --- /dev/null +++ b/sound/soc/codecs/framer-codec.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Framer ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina <herve.codina@bootlin.com> + +#include <linux/clk.h> +#include <linux/framer/framer.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define FRAMER_NB_CHANNEL 32 +#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT) + +struct framer_codec { + struct framer *framer; + struct device *dev; + struct snd_soc_jack jack; + struct notifier_block nb; + struct work_struct carrier_work; + int max_chan_playback; + int max_chan_capture; +}; + +static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + framer->max_chan_playback = hweight32(tx_mask); + if (framer->max_chan_playback > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n", + tx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + framer->max_chan_capture = hweight32(rx_mask); + if (framer->max_chan_capture > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n", + rx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + return 0; +} + +/* + * The constraints for format/channel is to match with the number of 8bit + * time-slots available. + */ +static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_pcm_format_t format = params_format(params); + struct snd_interval ch = {0}; + int width; + + width = snd_pcm_format_physical_width(format); + if (width == 8 || width == 16 || width == 32 || width == 64) { + ch.max = nb_ts * 8 / width; + } else { + dev_err(dai->dev, "format physical width %d not supported\n", width); + return -EINVAL; + } + + ch.min = ch.max ? 1 : 0; + + return snd_interval_refine(c, &ch); +} + +static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture); +} + +static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + unsigned int channels = params_channels(params); + unsigned int slot_width; + snd_pcm_format_t format; + struct snd_mask f_new; + + if (!channels || channels > nb_ts) { + dev_err(dai->dev, "channels %u not supported\n", nb_ts); + return -EINVAL; + } + + slot_width = (nb_ts / channels) * 8; + + snd_mask_none(&f_new); + pcm_for_each_format(format) { + if (snd_mask_test_format(f_old, format)) { + if (snd_pcm_format_physical_width(format) <= slot_width) + snd_mask_set_format(&f_new, format); + } + } + + return snd_mask_refine(f_old, &f_new); +} + +static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture); +} + +static u64 framer_formats(u8 nb_ts) +{ + unsigned int format_width; + unsigned int chan_width; + snd_pcm_format_t format; + u64 formats_mask; + + if (!nb_ts) + return 0; + + formats_mask = 0; + chan_width = nb_ts * 8; + pcm_for_each_format(format) { + /* Support physical width multiple of 8bit */ + format_width = snd_pcm_format_physical_width(format); + if (format_width == 0 || format_width % 8) + continue; + + /* + * And support physical width that can fit N times in the + * channel + */ + if (format_width > chan_width || chan_width % format_width) + continue; + + formats_mask |= pcm_format_to_bits(format); + } + return formats_mask; +} + +static int framer_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + snd_pcm_hw_rule_func_t hw_rule_channels_by_format; + snd_pcm_hw_rule_func_t hw_rule_format_by_channels; + unsigned int frame_bits; + u64 format; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + format = framer_formats(framer->max_chan_capture); + hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels; + frame_bits = framer->max_chan_capture * 8; + } else { + format = framer_formats(framer->max_chan_playback); + hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels; + frame_bits = framer->max_chan_playback * 8; + } + + ret = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, format); + if (ret) { + dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels_by_format, dai, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) { + dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format_by_channels, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) { + dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + frame_bits); + if (ret < 0) { + dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + return 0; +} + +static u64 framer_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops framer_dai_ops = { + .startup = framer_dai_startup, + .set_tdm_slot = framer_dai_set_tdm_slot, + .auto_selectable_formats = framer_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats), +}; + +static struct snd_soc_dai_driver framer_dai_driver = { + .name = "framer", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .ops = &framer_dai_ops, +}; + +static void framer_carrier_work(struct work_struct *work) +{ + struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work); + struct framer_status framer_status; + int jack_status; + int ret; + + ret = framer_get_status(framer->framer, &framer_status); + if (ret) { + dev_err(framer->dev, "get framer status failed (%d)\n", ret); + return; + } + + jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0; + snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK); +} + +static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct framer_codec *framer = container_of(nb, struct framer_codec, nb); + + switch (action) { + case FRAMER_EVENT_STATUS: + queue_work(system_power_efficient_wq, &framer->carrier_work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int framer_component_probe(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + struct framer_status status; + char *name; + int ret; + + INIT_WORK(&framer->carrier_work, framer_carrier_work); + + name = "carrier"; + if (component->name_prefix) { + name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix); + if (!name) + return -ENOMEM; + } + + ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack); + if (component->name_prefix) + kfree(name); /* A copy is done by snd_soc_card_jack_new */ + if (ret) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + + ret = framer_init(framer->framer); + if (ret) { + dev_err(component->dev, "framer init failed (%d)\n", ret); + return ret; + } + + ret = framer_power_on(framer->framer); + if (ret) { + dev_err(component->dev, "framer power-on failed (%d)\n", ret); + goto framer_exit; + } + + /* Be sure that get_status is supported */ + ret = framer_get_status(framer->framer, &status); + if (ret) { + dev_err(component->dev, "get framer status failed (%d)\n", ret); + goto framer_power_off; + } + + framer->nb.notifier_call = framer_carrier_notifier; + ret = framer_notifier_register(framer->framer, &framer->nb); + if (ret) { + dev_err(component->dev, "Cannot register event notifier\n"); + goto framer_power_off; + } + + /* Queue work to set the initial value */ + queue_work(system_power_efficient_wq, &framer->carrier_work); + + return 0; + +framer_power_off: + framer_power_off(framer->framer); +framer_exit: + framer_exit(framer->framer); + return ret; +} + +static void framer_component_remove(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + + framer_notifier_unregister(framer->framer, &framer->nb); + cancel_work_sync(&framer->carrier_work); + framer_power_off(framer->framer); + framer_exit(framer->framer); +} + +static const struct snd_soc_component_driver framer_component_driver = { + .probe = framer_component_probe, + .remove = framer_component_remove, + .endianness = 1, +}; + +static int framer_codec_probe(struct platform_device *pdev) +{ + struct framer_codec *framer; + + framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL); + if (!framer) + return -ENOMEM; + + framer->dev = &pdev->dev; + + /* Get framer from parents node */ + framer->framer = devm_framer_get(&pdev->dev, NULL); + if (IS_ERR(framer->framer)) + return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n"); + + platform_set_drvdata(pdev, framer); + + return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver, + &framer_dai_driver, 1); +} + +static struct platform_driver framer_codec_driver = { + .driver = { + .name = "framer-codec", + }, + .probe = framer_codec_probe, +}; +module_platform_driver(framer_codec_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("FRAMER ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index d2117e36ddd1..5a58723dc0e9 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -198,19 +198,19 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec, false); if (ret < 0) { - dev_err(&hdev->dev, "create hda codec failed: %d\n", ret); + dev_err(&hdev->dev, "codec create failed: %d\n", ret); goto device_new_err; } ret = snd_hda_codec_set_name(codec, codec->preset->name); if (ret < 0) { - dev_err(&hdev->dev, "name failed %s\n", codec->preset->name); + dev_err(&hdev->dev, "set name: %s failed: %d\n", codec->preset->name, ret); goto err; } ret = snd_hdac_regmap_init(&codec->core); if (ret < 0) { - dev_err(&hdev->dev, "regmap init failed\n"); + dev_err(&hdev->dev, "regmap init failed: %d\n", ret); goto err; } @@ -223,13 +223,13 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = patch(codec); if (ret < 0) { - dev_err(&hdev->dev, "patch failed %d\n", ret); + dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; } ret = snd_hda_codec_parse_pcms(codec); if (ret < 0) { - dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + dev_err(&hdev->dev, "unable to map pcms to dai: %d\n", ret); goto parse_pcms_err; } @@ -350,6 +350,11 @@ static int hda_hdev_attach(struct hdac_device *hdev) struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); struct snd_soc_component_driver *comp_drv; + if (hda_codec_is_display(codec) && !hdev->bus->audio_component) { + dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id); + return -ENODEV; + } + comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL); if (!comp_drv) return -ENOMEM; diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index d3684c7ab930..d98718b3dc4b 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -11,6 +11,13 @@ /* The soundwire block should be internally reset at probe */ #define LPASS_MACRO_FLAG_RESET_SWR BIT(1) +enum lpass_version { + LPASS_VER_9_0_0, + LPASS_VER_9_2_0, + LPASS_VER_10_0_0, + LPASS_VER_11_0_0, +}; + struct lpass_macro { struct device *macro_pd; struct device *dcodec_pd; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 124c2e144f33..c98b0b747a92 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -38,6 +38,8 @@ #define CDC_TX_TOP_CSR_I2S_RESET (0x00AC) #define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4) #define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0) +/* Default divider for AMIC and DMIC clock: DIV2 */ +#define CDC_TX_SWR_MIC_CLK_DEFAULT 0 #define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1) #define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4) #define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8) @@ -253,8 +255,18 @@ struct hpf_work { struct delayed_work dwork; }; +struct tx_macro_data { + unsigned int flags; + unsigned int ver; + const struct snd_soc_dapm_widget *extra_widgets; + size_t extra_widgets_num; + const struct snd_soc_dapm_route *extra_routes; + size_t extra_routes_num; +}; + struct tx_macro { struct device *dev; + const struct tx_macro_data *data; struct snd_soc_component *component; struct hpf_work tx_hpf_work[NUM_DECIMATORS]; struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; @@ -270,7 +282,6 @@ struct tx_macro { struct clk_hw hw; bool dec_active[NUM_DECIMATORS]; int tx_mclk_users; - u16 dmic_clk_div; bool bcs_enable; int dec_mode[NUM_DECIMATORS]; struct lpass_macro *pds; @@ -431,6 +442,8 @@ static bool tx_is_volatile_register(struct device *dev, unsigned int reg) case CDC_TX_TOP_CSR_SWR_DMIC1_CTL: case CDC_TX_TOP_CSR_SWR_DMIC2_CTL: case CDC_TX_TOP_CSR_SWR_DMIC3_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: return true; } return false; @@ -635,13 +648,18 @@ exit: return 0; } -static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator) +static bool is_amic_enabled(struct snd_soc_component *component, + struct tx_macro *tx, u8 decimator) { u16 adc_mux_reg, adc_reg, adc_n; adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator); if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) { + if (tx->data->ver > LPASS_VER_9_0_0) + return true; + + /* else: LPASS <= v9.0.0 */ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator); adc_n = snd_soc_component_read_field(component, adc_reg, CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK); @@ -670,7 +688,7 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator); hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator); - if (is_amic_enabled(component, hpf_work->decimator)) { + if (is_amic_enabled(component, tx, hpf_work->decimator)) { snd_soc_component_write_field(component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, @@ -734,16 +752,61 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, return 0; } +static void tx_macro_update_smic_sel_v9(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (val < 5) { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } +} + +static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (widget->shift) { + /* MSM DMIC */ + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } +} + static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, dmic; - u16 mic_sel_reg; - u16 dmic_clk_reg; struct tx_macro *tx = snd_soc_component_get_drvdata(component); + unsigned int val; + u16 mic_sel_reg; val = ucontrol->value.enumerated.item[0]; if (val >= e->items) @@ -780,21 +843,15 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, } if (val != 0) { - if (widget->shift) { /* MSM DMIC */ - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 1); - } else if (val < 5) { - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 0); - } else { + if (widget->shift) /* MSM DMIC */ snd_soc_component_write_field(component, mic_sel_reg, CDC_TXn_ADC_DMIC_SEL_MASK, 1); - dmic = TX_ADC_TO_DMIC(val); - dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); - snd_soc_component_write_field(component, dmic_clk_reg, - CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); - } + else if (tx->data->ver <= LPASS_VER_9_0_0) + tx_macro_update_smic_sel_v9(component, widget, tx, + mic_sel_reg, val); + else + tx_macro_update_smic_sel_v9_2(component, widget, tx, + mic_sel_reg, val); } return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); @@ -882,7 +939,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_write_field(component, dmic_clk_reg, CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); + CDC_TX_SWR_MIC_CLK_DEFAULT); } } snd_soc_component_write_field(component, dec_cfg_reg, @@ -895,7 +952,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_component_write_field(component, tx_vol_ctl_reg, CDC_TXn_CLK_EN_MASK, 0x1); - if (!is_amic_enabled(component, decimator)) { + if (!is_amic_enabled(component, tx, decimator)) { snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00); /* Minimum 1 clk cycle delay is required as per HW spec */ usleep_range(1000, 1010); @@ -911,7 +968,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_CUT_FREQ_MASK, CF_MIN_3DB_150HZ); - if (is_amic_enabled(component, decimator)) { + if (is_amic_enabled(component, tx, decimator)) { hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS; unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS; } @@ -927,7 +984,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, 0x02); - if (!is_amic_enabled(component, decimator)) + if (!is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, @@ -964,7 +1021,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, hpf_cut_off_freq); - if (is_amic_enabled(component, decimator)) + if (is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | @@ -1235,53 +1292,6 @@ static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_d static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum); static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum); -static const char * const smic_mux_text[] = { - "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", - "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", - "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" -}; - -static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0, - 0, smic_mux_text); - -static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); - static const char * const dmic_mux_text[] = { "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "DMIC7" @@ -1427,15 +1437,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0, tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)), - SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux), - SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux), SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux), SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux), @@ -1445,18 +1446,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux), SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux), - SND_SOC_DAPM_INPUT("TX SWR_ADC0"), - SND_SOC_DAPM_INPUT("TX SWR_ADC1"), - SND_SOC_DAPM_INPUT("TX SWR_ADC2"), - SND_SOC_DAPM_INPUT("TX SWR_ADC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), SND_SOC_DAPM_INPUT("TX DMIC0"), SND_SOC_DAPM_INPUT("TX DMIC1"), SND_SOC_DAPM_INPUT("TX DMIC2"), @@ -1578,6 +1567,150 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, +}; + +/* Controls and routes specific to LPASS <= v9.0.0 */ +static const char * const smic_mux_text_v9[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", + "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", + "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9); + +static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9[] = { {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, @@ -1593,16 +1726,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, - {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, - {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, @@ -1618,16 +1741,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, - {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, - {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, @@ -1643,16 +1756,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, - {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, - {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, @@ -1668,16 +1771,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, - {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, - {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, @@ -1693,16 +1786,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, - {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, - {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, @@ -1718,16 +1801,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, - {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, - {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, @@ -1743,16 +1816,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, - {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, - {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, @@ -1769,6 +1832,200 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, }; +/* Controls and routes specific to LPASS >= v9.2.0 */ +static const char * const smic_mux_text_v9_2[] = { + "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3", + "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7", + "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9_2); + +static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2), + + SND_SOC_DAPM_INPUT("TX SWR_INPUT0"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT1"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT2"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT3"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT4"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT5"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT6"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT7"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT8"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT9"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT10"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT11"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = { + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"}, + {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"}, +}; + static const struct snd_kcontrol_new tx_macro_snd_controls[] = { SOC_SINGLE_S8_TLV("TX_DEC0 Volume", CDC_TX0_TX_VOL_CTL, @@ -1823,10 +2080,41 @@ static const struct snd_kcontrol_new tx_macro_snd_controls[] = { tx_macro_get_bcs, tx_macro_set_bcs), }; +static int tx_macro_component_extend(struct snd_soc_component *comp) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp); + struct tx_macro *tx = snd_soc_component_get_drvdata(comp); + int ret; + + if (tx->data->extra_widgets_num) { + ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets, + tx->data->extra_widgets_num); + if (ret) { + dev_err(tx->dev, "failed to add extra widgets: %d\n", ret); + return ret; + } + } + + if (tx->data->extra_routes_num) { + ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes, + tx->data->extra_routes_num); + if (ret) { + dev_err(tx->dev, "failed to add extra routes: %d\n", ret); + return ret; + } + } + + return 0; +} + static int tx_macro_component_probe(struct snd_soc_component *comp) { struct tx_macro *tx = snd_soc_component_get_drvdata(comp); - int i; + int i, ret; + + ret = tx_macro_component_extend(comp); + if (ret) + return ret; snd_soc_component_init_regmap(comp, tx->regmap); @@ -1848,8 +2136,10 @@ static int tx_macro_component_probe(struct snd_soc_component *comp) snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F, 0x0A); /* Enable swr mic0 and mic1 clock */ - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00); - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); return 0; } @@ -1954,17 +2244,16 @@ static int tx_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - kernel_ulong_t flags; struct tx_macro *tx; void __iomem *base; int ret, reg; - flags = (kernel_ulong_t)device_get_match_data(dev); - tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL); if (!tx) return -ENOMEM; + tx->data = device_get_match_data(dev); + tx->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(tx->macro)) return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); @@ -1977,7 +2266,7 @@ static int tx_macro_probe(struct platform_device *pdev) if (IS_ERR(tx->mclk)) return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); - if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { tx->npl = devm_clk_get(dev, "npl"); if (IS_ERR(tx->npl)) return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); @@ -2052,7 +2341,7 @@ static int tx_macro_probe(struct platform_device *pdev) /* reset soundwire block */ - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); @@ -2060,7 +2349,7 @@ static int tx_macro_probe(struct platform_device *pdev) CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, 0x0); @@ -2164,25 +2453,75 @@ static const struct dev_pm_ops tx_macro_pm_ops = { SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) }; +static const struct tx_macro_data lpass_ver_9 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), + .extra_routes = tx_audio_map_v9, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), +}; + +static const struct tx_macro_data lpass_ver_9_2 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_2_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + +static const struct tx_macro_data lpass_ver_10_sm6115 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .ver = LPASS_VER_10_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + +static const struct tx_macro_data lpass_ver_11 = { + .flags = LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_11_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + static const struct of_device_id tx_macro_dt_match[] = { { + /* + * The block is actually LPASS v9.4, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC7280 fine, so apparently the fix + * is not necessary. + */ .compatible = "qcom,sc7280-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm6115-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .data = &lpass_ver_10_sm6115, }, { .compatible = "qcom,sm8250-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm8450-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9_2, }, { .compatible = "qcom,sm8550-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_RESET_SWR, + .data = &lpass_ver_11, }, { .compatible = "qcom,sc8280xp-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + /* + * The block is actually LPASS v9.3, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC8280xp fine, so apparently the + * fix is not necessary. + */ + .data = &lpass_ver_9, }, { } }; diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index b71ef03c4aef..6eceeff10bf6 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -201,10 +201,12 @@ struct va_macro { unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; u16 dmic_clk_div; bool has_swr_master; + bool has_npl_clk; int dec_mode[VA_MACRO_NUM_DECIMATORS]; struct regmap *regmap; struct clk *mclk; + struct clk *npl; struct clk *macro; struct clk *dcodec; struct clk *fsgen; @@ -225,14 +227,22 @@ struct va_macro { struct va_macro_data { bool has_swr_master; + bool has_npl_clk; }; static const struct va_macro_data sm8250_va_data = { .has_swr_master = false, + .has_npl_clk = false, }; static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, + .has_npl_clk = true, +}; + +static const struct va_macro_data sm8550_va_data = { + .has_swr_master = true, + .has_npl_clk = false, }; static bool va_is_volatile_register(struct device *dev, unsigned int reg) @@ -1332,6 +1342,12 @@ static int fsgen_gate_enable(struct clk_hw *hw) struct regmap *regmap = va->regmap; int ret; + if (va->has_swr_master) { + ret = clk_prepare_enable(va->mclk); + if (ret) + return ret; + } + ret = va_macro_mclk_enable(va, true); if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, @@ -1350,6 +1366,8 @@ static void fsgen_gate_disable(struct clk_hw *hw) CDC_VA_SWR_CLK_EN_MASK, 0x0); va_macro_mclk_enable(va, false); + if (va->has_swr_master) + clk_disable_unprepare(va->mclk); } static int fsgen_gate_is_enabled(struct clk_hw *hw) @@ -1378,6 +1396,9 @@ static int va_macro_register_fsgen_output(struct va_macro *va) struct clk_init_data init; int ret; + if (va->has_npl_clk) + parent = va->npl; + parent_clk_name = __clk_get_name(parent); of_property_read_string(np, "clock-output-names", &clk_name); @@ -1500,10 +1521,21 @@ static int va_macro_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); va->has_swr_master = data->has_swr_master; + va->has_npl_clk = data->has_npl_clk; /* mclk rate */ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + if (va->has_npl_clk) { + va->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(va->npl)) { + ret = PTR_ERR(va->npl); + goto err; + } + + clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + } + ret = clk_prepare_enable(va->macro); if (ret) goto err; @@ -1516,6 +1548,12 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_mclk; + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) + goto err_npl; + } + if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, @@ -1564,6 +1602,9 @@ static int va_macro_probe(struct platform_device *pdev) return 0; err_clkout: + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); +err_npl: clk_disable_unprepare(va->mclk); err_mclk: clk_disable_unprepare(va->dcodec); @@ -1579,6 +1620,9 @@ static void va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); clk_disable_unprepare(va->dcodec); clk_disable_unprepare(va->macro); @@ -1593,6 +1637,9 @@ static int __maybe_unused va_macro_runtime_suspend(struct device *dev) regcache_cache_only(va->regmap, true); regcache_mark_dirty(va->regmap); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); return 0; @@ -1609,6 +1656,15 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) return ret; } + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) { + clk_disable_unprepare(va->mclk); + dev_err(va->dev, "unable to prepare npl\n"); + return ret; + } + } + regcache_cache_only(va->regmap, false); regcache_sync(va->regmap); @@ -1624,6 +1680,7 @@ static const struct of_device_id va_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, + { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data }, { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data }, {} }; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index a2cca3436c68..950105e5bffd 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -314,7 +314,7 @@ static int max98363_update_status(struct sdw_slave *slave, return max98363_io_init(slave); } -static struct sdw_slave_ops max98363_slave_ops = { +static const struct sdw_slave_ops max98363_slave_ops = { .read_prop = max98363_read_prop, .update_status = max98363_update_status, }; diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index b5cb951af570..383e551f3bc7 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -821,7 +821,7 @@ static int max98373_bus_config(struct sdw_slave *slave, * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now */ -static struct sdw_slave_ops max98373_slave_ops = { +static const struct sdw_slave_ops max98373_slave_ops = { .read_prop = max98373_read_prop, .update_status = max98373_update_status, .bus_config = max98373_bus_config, diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index f66417a0f29f..22251fb2fa1f 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -26,7 +26,6 @@ #include <sound/tlv.h> #include "nau8540.h" - #define NAU_FREF_MAX 13500000 #define NAU_FVCO_MAX 100000000 #define NAU_FVCO_MIN 90000000 @@ -230,6 +229,49 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new digital_ch1_mux = SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); +static int nau8540_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF | + NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF | + NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF | + NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF | + NAU8540_ACDC_CTL_MIC4N_VREF); + break; + default: + break; + } + return 0; +} + +static int nau8540_precharge_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, 0); + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, 0); + break; + default: + break; + } + return 0; +} + static int adc_power_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -237,8 +279,10 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); if (SND_SOC_DAPM_EVENT_ON(event)) { - msleep(300); + msleep(160); /* DO12 and DO34 pad output enable */ + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, NAU8540_I2S_DO12_TRI, 0); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, @@ -248,6 +292,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, 0); } return 0; } @@ -274,28 +320,26 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC3"), SND_SOC_DAPM_INPUT("MIC4"), - SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), - - SND_SOC_DAPM_ADC_E("ADC1", NULL, - NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC2", NULL, - NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC3", NULL, - NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC4", NULL, - NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0, + nau8540_precharge_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_MUX("Digital CH4 Mux", SND_SOC_NOPM, 0, 0, &digital_ch4_mux), @@ -316,20 +360,20 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { {"Frontend PGA3", NULL, "MIC3"}, {"Frontend PGA4", NULL, "MIC4"}, - {"ADC1", NULL, "Frontend PGA1"}, - {"ADC2", NULL, "Frontend PGA2"}, - {"ADC3", NULL, "Frontend PGA3"}, - {"ADC4", NULL, "Frontend PGA4"}, + {"Precharge", NULL, "Frontend PGA1"}, + {"Precharge", NULL, "Frontend PGA2"}, + {"Precharge", NULL, "Frontend PGA3"}, + {"Precharge", NULL, "Frontend PGA4"}, - {"ADC CH1", NULL, "ADC1"}, - {"ADC CH2", NULL, "ADC2"}, - {"ADC CH3", NULL, "ADC3"}, - {"ADC CH4", NULL, "ADC4"}, + {"ADC CH1", NULL, "Precharge"}, + {"ADC CH2", NULL, "Precharge"}, + {"ADC CH3", NULL, "Precharge"}, + {"ADC CH4", NULL, "Precharge"}, - {"ADC1", NULL, "MICBIAS1"}, - {"ADC2", NULL, "MICBIAS1"}, - {"ADC3", NULL, "MICBIAS2"}, - {"ADC4", NULL, "MICBIAS2"}, + {"ADC CH1", NULL, "MICBIAS1"}, + {"ADC CH2", NULL, "MICBIAS1"}, + {"ADC CH3", NULL, "MICBIAS2"}, + {"ADC CH4", NULL, "MICBIAS2"}, {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"}, {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"}, diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h index 2ce6063d462b..762bb93b06fd 100644 --- a/sound/soc/codecs/nau8540.h +++ b/sound/soc/codecs/nau8540.h @@ -78,6 +78,7 @@ /* POWER_MANAGEMENT (0x01) */ +#define NAU8540_ADC_ALL_EN 0xf #define NAU8540_ADC4_EN (0x1 << 3) #define NAU8540_ADC3_EN (0x1 << 2) #define NAU8540_ADC2_EN (0x1 << 1) @@ -202,6 +203,7 @@ /* REFERENCE (0x68) */ #define NAU8540_PRECHARGE_DIS (0x1 << 13) #define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8540_DISCHRG_EN (0x1 << 11) /* FEPGA1 (0x69) */ #define NAU8540_FEPGA1_MODCH2_SHT_SFT 7 @@ -214,7 +216,16 @@ #define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT) #define NAU8540_FEPGA2_MODCH3_SHT_SFT 3 #define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT) - +#define NAU8540_ACDC_CTL_SFT 8 +#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT) +#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15) +#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14) +#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13) +#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12) +#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11) +#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10) +#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9) +#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8) /* System Clock Source */ enum { diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 5cb0de648bd3..cd30ad649bae 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2836,16 +2836,12 @@ static int nau8825_read_device_properties(struct device *dev, if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500) dev_warn(dev, "Please set the suitable delay time!\n"); - nau8825->mclk = devm_clk_get(dev, "mclk"); - if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + nau8825->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(nau8825->mclk)) + return PTR_ERR(nau8825->mclk); + if (!nau8825->mclk) /* The MCLK is managed externally or not used at all */ - nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); - } else if (IS_ERR(nau8825->mclk)) { - return -EINVAL; - } return 0; } diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 7295f44c77eb..4dbbd8bdaaac 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -520,7 +520,7 @@ static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { { "DP2TX", NULL, "V Sense" }, }; -static struct sdw_slave_ops rt1017_sdca_slave_ops = { +static const struct sdw_slave_ops rt1017_sdca_slave_ops = { .read_prop = rt1017_sdca_read_prop, .update_status = rt1017_sdca_update_status, }; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 9a33e3776b55..6e7843484250 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1192,7 +1192,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 981155b046fd..f8994f4968c5 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1237,7 +1237,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index ad3783ade1b5..03d9839a5de3 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1284,7 +1284,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 3ee6d85268ba..f475c8cfadae 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -279,7 +279,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component) rt5514_dsp); if (ret) dev_err(&rt5514_spi->dev, - "%s Failed to reguest IRQ: %d\n", __func__, + "%s Failed to request IRQ: %d\n", __func__, ret); else device_init_wakeup(rt5514_dsp->dev, true); diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 43fc7814fdde..a8cdc3d6994d 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1054,9 +1054,6 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_PREPARE: - if (IS_ERR(rt5514->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5514->mclk); } else { @@ -1097,9 +1094,9 @@ static int rt5514_probe(struct snd_soc_component *component) struct platform_device *pdev = container_of(component->dev, struct platform_device, dev); - rt5514->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5514->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5514->mclk)) + return PTR_ERR(rt5514->mclk); if (rt5514->pdata.dsp_calib_clk_name) { rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev, diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index c13108b51eaf..e7aa60e73961 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1174,9 +1174,6 @@ static int rt5616_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5616->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5616->mclk); } else { @@ -1225,9 +1222,9 @@ static int rt5616_probe(struct snd_soc_component *component) struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); /* Check if MCLK provided */ - rt5616->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5616->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5616->mclk)) + return PTR_ERR(rt5616->mclk); rt5616->component = component; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e8cdc166bdaa..174872ef35d2 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1949,9 +1949,6 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5640->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5640->mclk); } else { @@ -2661,9 +2658,9 @@ static int rt5640_probe(struct snd_soc_component *component) u32 val; /* Check if MCLK provided */ - rt5640->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5640->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5640->mclk)) + return PTR_ERR(rt5640->mclk); rt5640->component = component; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 20191a4473c2..e3ba04484813 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -4223,7 +4223,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5645", rt5645); if (ret) { - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); goto err_enable; } } diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 0cee4fd1c84b..33a34bd0b405 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2261,7 +2261,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651); if (ret) { - dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n", rt5651->irq, ret); rt5651->irq = -ENXIO; } diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index a061028a16d8..fb094c0fe740 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4292,7 +4292,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5659", rt5659); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); /* Enable IRQ output for GPIO1 pin any way */ regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 0cecfd602415..d5c2f0f2df98 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1079,9 +1079,6 @@ static int rt5660_set_bias_level(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5660_GEN_CTRL1, RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL); - if (IS_ERR(rt5660->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5660->mclk); } else { @@ -1277,9 +1274,9 @@ static int rt5660_i2c_probe(struct i2c_client *i2c) return -ENOMEM; /* Check if MCLK provided */ - rt5660->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5660->mclk)) + return PTR_ERR(rt5660->mclk); i2c_set_clientdata(i2c, rt5660); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 9550492605ac..161dcb3915f9 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3692,7 +3692,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5663", rt5663); if (ret) { - dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", + dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n", __func__, ret); goto err_enable; } diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index a39de4a7df00..6f778c8f0832 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4929,7 +4929,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5665", rt5665); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 4623b3e62487..6d8e228ccb57 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2580,7 +2580,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5668", rt5668); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index fbad1ed06626..62f26ce9d476 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -266,7 +266,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } #ifdef CONFIG_COMMON_CLK diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 3322056bbb3b..12741668fdb3 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -3283,7 +3283,7 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682s->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev, diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index ba08d03e717c..0926b26619bd 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -944,7 +944,7 @@ static const struct dev_pm_ops rt712_sdca_dmic_pm = { }; -static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { .read_prop = rt712_sdca_dmic_read_prop, .update_status = rt712_sdca_dmic_update_status, }; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6b644a89c589..01ac555cd79b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -331,7 +331,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt712_sdca_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_slave_ops = { .read_prop = rt712_sdca_read_prop, .interrupt_callback = rt712_sdca_interrupt_callback, .update_status = rt712_sdca_update_status, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e24b9cbdc10c..eb76f4c675b6 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -362,7 +362,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt722_sdca_slave_ops = { +static const struct sdw_slave_ops rt722_sdca_slave_ops = { .read_prop = rt722_sdca_read_prop, .interrupt_callback = rt722_sdca_interrupt_callback, .update_status = rt722_sdca_update_status, diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 5d0e5348b361..3aa81514dad7 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -408,7 +408,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv) { if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) gpio_free(tas_priv->irq_info.irq_gpio); - kfree(tas_priv->acpi_subsystem_id); mutex_destroy(&tas_priv->codec_lock); } EXPORT_SYMBOL_GPL(tasdevice_remove); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 85e14ff61769..45760fe19523 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -1531,7 +1531,7 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv, unsigned int sleep_time; unsigned int len; unsigned int nr_cmds; - unsigned char *data = block->data; + unsigned char *data; unsigned char crc_chksum = 0; unsigned char offset; unsigned char book; diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h index 4e3653058275..eeb9bc5b01e2 100644 --- a/sound/soc/codecs/wcd-clsh-v2.h +++ b/sound/soc/codecs/wcd-clsh-v2.h @@ -47,6 +47,7 @@ enum wcd_codec_version { /* New CLSH after this */ WCD937X = 2, WCD938X = 3, + WCD939X = 4, }; struct wcd_clsh_ctrl; diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 5da1934527f3..0e6218ed0e5e 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -16,6 +16,7 @@ #define HS_DETECT_PLUG_TIME_MS (3 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 +#define GND_MIC_USBC_SWAP_THRESHOLD 2 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HPHL_CROSS_CONN_THRESHOLD 100 #define HS_VREF_MIN_VAL 1400 @@ -52,12 +53,15 @@ struct wcd_mbhc { struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; + /* Work to handle plug report */ + struct work_struct mbhc_plug_detect_work; /* Work to correct accessory type */ struct work_struct correct_plug_swch; struct mutex lock; int buttons_pressed; u32 hph_status; /* track headhpone status */ u8 current_plug; + unsigned int swap_thr; bool is_btn_press; bool in_swch_irq_handler; bool hs_detect_work_stop; @@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) } } -static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +static void mbhc_plug_detect_fn(struct work_struct *work) { - struct snd_soc_component *component; + struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work); + struct snd_soc_component *component = mbhc->component; enum snd_jack_types jack_type; - struct wcd_mbhc *mbhc = data; bool detection_type; - component = mbhc->component; mutex_lock(&mbhc->lock); mbhc->in_swch_irq_handler = true; @@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) exit: mbhc->in_swch_irq_handler = false; mutex_unlock(&mbhc->lock); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + if (!mbhc->cfg->typec_analog_mux) + schedule_work(&mbhc->mbhc_plug_detect_work); + return IRQ_HANDLED; } +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc) +{ + + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, false); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug); + +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc) +{ + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, true); + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug); + static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) { int mask = 0; @@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); - /* enable HS detection */ + if (mbhc->cfg->typec_analog_mux) + mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; + else + mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD; + + /* setup HS detection */ if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(component, - HS_PULLUP_I_DEFAULT); + mbhc->cfg->typec_analog_mux ? + HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT); else if (mbhc->mbhc_cb->hph_pull_up_control) - mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT); + mbhc->mbhc_cb->hph_pull_up_control(component, + mbhc->cfg->typec_analog_mux ? + I_OFF : I_DEFAULT); else - wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + mbhc->cfg->typec_analog_mux ? 0 : 3); wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh); wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh); @@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true); wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); - wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + /* Plug detect is triggered manually if analog goes through USBCC */ + if (mbhc->cfg->typec_analog_mux) + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); - /* Insertion debounce set to 96ms */ - wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); + if (mbhc->cfg->typec_analog_mux) + /* Insertion debounce set to 48ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4); + else + /* Insertion debounce set to 96ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); /* Button Debounce set to 16ms */ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2); @@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_bias(component, true); /* enable MBHC clock */ if (mbhc->mbhc_cb->clk_setup) - mbhc->mbhc_cb->clk_setup(component, true); + mbhc->mbhc_cb->clk_setup(component, + mbhc->cfg->typec_analog_mux ? false : true); /* program HS_VREF value */ wcd_program_hs_vref(mbhc); @@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) do { cross_conn = wcd_check_cross_conn(mbhc); try++; - } while (try < GND_MIC_SWAP_THRESHOLD); + } while (try < mbhc->swap_thr); if (cross_conn > 0) { plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1183,7 +1246,7 @@ correct_plug_type: cross_conn = wcd_check_cross_conn(mbhc); if (cross_conn > 0) { /* cross-connection */ pt_gnd_mic_swap_cnt++; - if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD) + if (pt_gnd_mic_swap_cnt < mbhc->swap_thr) continue; else plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1194,7 +1257,7 @@ correct_plug_type: } else /* Error if (cross_conn < 0) */ continue; - if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) { + if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) { /* US_EU gpio present, flip switch */ if (mbhc->cfg->swap_gnd_mic) { if (mbhc->cfg->swap_gnd_mic(component, true)) @@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, mutex_init(&mbhc->lock); INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); + INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn); ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL, wcd_mbhc_mech_plug_detect_irq, @@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + cancel_work_sync(&mbhc->mbhc_plug_detect_work); mutex_unlock(&mbhc->lock); kfree(mbhc); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 006118f3e81f..df68e99c81a3 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -193,6 +193,7 @@ struct wcd_mbhc_config { int v_hs_max; int num_btn; bool mono_stero_detection; + bool typec_analog_mux; bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active); bool hs_ext_micbias; bool gnd_det_en; @@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, void wcd_mbhc_stop(struct wcd_mbhc *mbhc); void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type); int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c new file mode 100644 index 000000000000..8acb5651c5bc --- /dev/null +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -0,0 +1,1551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/component.h> +#include <linux/pm_runtime.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include "wcd939x.h" + +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) + +static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_L, WCD939X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_R, WCD939X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_LO, WCD939X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_L, WCD939X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_R, WCD939X_DSD_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_HIFI_PCM_L, WCD939X_HIFI_PCM_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), +}; + +static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_ADC4, WCD939X_ADC_1_4_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC0, WCD939X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC1, WCD939X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_MBHC, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC2, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC3, WCD939X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC4, WCD939X_DMIC_3_7_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC5, WCD939X_DMIC_3_7_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_DMIC6, WCD939X_DMIC_3_7_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC7, WCD939X_DMIC_3_7_PORT, BIT(3)), +}; + +static struct sdw_dpn_prop wcd939x_rx_dpn_prop[WCD939X_MAX_RX_SWR_PORTS] = { + { + .num = WCD939X_HPH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_CLSH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_COMP_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_LO_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DSD_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_HIFI_PCM_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + } +}; + +static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = { + { + .num = WCD939X_ADC_1_4_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_ADC_DMIC_1_2_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_0_3_MBHC_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_3_7_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_device_get); + +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return FIELD_GET(SDW_SCP_STAT_CURR_BANK, + sdw_read(sdev, SDW_SCP_CTRL)); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_current_bank); + +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD939X_MAX_SWR_PORTS; i++) { + ch_mask = wcd->port_config[i].ch_mask; + + if (!ch_mask) + continue; + + for_each_set_bit(j, &ch_mask, 4) + wcd->sconfig.ch_count++; + + port_config[wcd->active_ports] = wcd->port_config[i]; + wcd->active_ports++; + } + + wcd->sconfig.bps = 1; + wcd->sconfig.frame_rate = params_rate(params); + if (wcd->is_tx) + wcd->sconfig.direction = SDW_DATA_DIR_TX; + else + wcd->sconfig.direction = SDW_DATA_DIR_RX; + + wcd->sconfig.type = SDW_STREAM_PCM; + + return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, &port_config[0], + wcd->active_ports, wcd->sruntime); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_hw_params); + +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_free); + +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, void *stream, + int direction) +{ + wcd->sruntime = stream; + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + if (wcd->regmap) + return wcd->regmap; + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_regmap); + +static int wcd9390_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + + if (wcd->regmap && status == SDW_SLAVE_ATTACHED) { + /* Write out any cached changes that happened between probe and attach */ + regcache_cache_only(wcd->regmap, false); + return regcache_sync(wcd->regmap); + } + + return 0; +} + +static int wcd9390_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +/* + * Handle Soundwire out-of-band interrupt event by triggering + * the first irq of the slave_irq irq domain, which then will + * be handled by the regmap_irq threaded irq. + * Looping is to ensure no interrupts were missed in the process. + */ +static int wcd9390_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + struct irq_domain *slave_irq = wcd->slave_irq; + u32 sts1, sts2, sts3; + + do { + handle_nested_irq(irq_find_mapping(slave_irq, 0)); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd939x_defaults[] = { + /* Default values except for Read-Only & Volatile registers */ + { WCD939X_ANA_PAGE, 0x00 }, + { WCD939X_ANA_BIAS, 0x00 }, + { WCD939X_ANA_RX_SUPPLIES, 0x00 }, + { WCD939X_ANA_HPH, 0x0c }, + { WCD939X_ANA_EAR, 0x00 }, + { WCD939X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD939X_ANA_TX_CH1, 0x20 }, + { WCD939X_ANA_TX_CH2, 0x00 }, + { WCD939X_ANA_TX_CH3, 0x20 }, + { WCD939X_ANA_TX_CH4, 0x00 }, + { WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MBHC_MECH, 0x39 }, + { WCD939X_ANA_MBHC_ELECT, 0x08 }, + { WCD939X_ANA_MBHC_ZDET, 0x00 }, + { WCD939X_ANA_MBHC_BTN0, 0x00 }, + { WCD939X_ANA_MBHC_BTN1, 0x10 }, + { WCD939X_ANA_MBHC_BTN2, 0x20 }, + { WCD939X_ANA_MBHC_BTN3, 0x30 }, + { WCD939X_ANA_MBHC_BTN4, 0x40 }, + { WCD939X_ANA_MBHC_BTN5, 0x50 }, + { WCD939X_ANA_MBHC_BTN6, 0x60 }, + { WCD939X_ANA_MBHC_BTN7, 0x70 }, + { WCD939X_ANA_MICB1, 0x10 }, + { WCD939X_ANA_MICB2, 0x10 }, + { WCD939X_ANA_MICB2_RAMP, 0x00 }, + { WCD939X_ANA_MICB3, 0x00 }, + { WCD939X_ANA_MICB4, 0x00 }, + { WCD939X_BIAS_CTL, 0x2a }, + { WCD939X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD939X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD939X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD939X_MBHC_CTL_CLK, 0x00 }, + { WCD939X_MBHC_CTL_ANA, 0x00 }, + { WCD939X_MBHC_ZDET_VNEG_CTL, 0x00 }, + { WCD939X_MBHC_ZDET_BIAS_CTL, 0x46 }, + { WCD939X_MBHC_CTL_BCS, 0x00 }, + { WCD939X_MBHC_TEST_CTL, 0x00 }, + { WCD939X_LDOH_MODE, 0x2b }, + { WCD939X_LDOH_BIAS, 0x68 }, + { WCD939X_LDOH_STB_LOADS, 0x00 }, + { WCD939X_LDOH_SLOWRAMP, 0x50 }, + { WCD939X_MICB1_TEST_CTL_1, 0x1a }, + { WCD939X_MICB1_TEST_CTL_2, 0x00 }, + { WCD939X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD939X_MICB2_TEST_CTL_1, 0x1a }, + { WCD939X_MICB2_TEST_CTL_2, 0x00 }, + { WCD939X_MICB2_TEST_CTL_3, 0x24 }, + { WCD939X_MICB3_TEST_CTL_1, 0x9a }, + { WCD939X_MICB3_TEST_CTL_2, 0x80 }, + { WCD939X_MICB3_TEST_CTL_3, 0x24 }, + { WCD939X_MICB4_TEST_CTL_1, 0x1a }, + { WCD939X_MICB4_TEST_CTL_2, 0x80 }, + { WCD939X_MICB4_TEST_CTL_3, 0x24 }, + { WCD939X_TX_COM_ADC_VCM, 0x39 }, + { WCD939X_TX_COM_BIAS_ATEST, 0xe0 }, + { WCD939X_TX_COM_SPARE1, 0x00 }, + { WCD939X_TX_COM_SPARE2, 0x00 }, + { WCD939X_TX_COM_TXFE_DIV_CTL, 0x22 }, + { WCD939X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD939X_TX_COM_SPARE3, 0x00 }, + { WCD939X_TX_COM_SPARE4, 0x00 }, + { WCD939X_TX_1_2_TEST_EN, 0xcc }, + { WCD939X_TX_1_2_ADC_IB, 0xe9 }, + { WCD939X_TX_1_2_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_1_2_TEST_CTL, 0x38 }, + { WCD939X_TX_1_2_TEST_BLK_EN1, 0xff }, + { WCD939X_TX_1_2_TXFE1_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_EN, 0xcc }, + { WCD939X_TX_3_4_ADC_IB, 0xe9 }, + { WCD939X_TX_3_4_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_3_4_TEST_CTL, 0x38 }, + { WCD939X_TX_3_4_TEST_BLK_EN3, 0xff }, + { WCD939X_TX_3_4_TXFE3_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN2, 0xfb }, + { WCD939X_TX_3_4_TXFE2_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE1, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN4, 0xfb }, + { WCD939X_TX_3_4_TXFE4_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE2, 0x00 }, + { WCD939X_CLASSH_MODE_1, 0x40 }, + { WCD939X_CLASSH_MODE_2, 0x3a }, + { WCD939X_CLASSH_MODE_3, 0xf0 }, + { WCD939X_CLASSH_CTRL_VCL_1, 0x7c }, + { WCD939X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD939X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD939X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD939X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD939X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD939X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD939X_CLASSH_SPARE, 0x80 }, + { WCD939X_FLYBACK_EN, 0x4e }, + { WCD939X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD939X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD939X_FLYBACK_VNEG_CTRL_3, 0x14 }, + { WCD939X_FLYBACK_VNEG_CTRL_4, 0xdb }, + { WCD939X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD939X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD939X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD939X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD939X_FLYBACK_VNEG_CTRL_9, 0x66 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_2, 0xf8 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD939X_FLYBACK_CTRL_1, 0x65 }, + { WCD939X_FLYBACK_TEST_CTL, 0x02 }, + { WCD939X_RX_AUX_SW_CTL, 0x00 }, + { WCD939X_RX_PA_AUX_IN_CONN, 0x01 }, + { WCD939X_RX_TIMER_DIV, 0x32 }, + { WCD939X_RX_OCP_CTL, 0x1f }, + { WCD939X_RX_OCP_COUNT, 0x77 }, + { WCD939X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD939X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD939X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD939X_RX_BIAS_HPH_PA, 0xaa }, + { WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2, 0xca }, + { WCD939X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD939X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD939X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD939X_RX_BIAS_AUX_DAC, 0xa0 }, + { WCD939X_RX_BIAS_AUX_AMP, 0xaa }, + { WCD939X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD939X_RX_BIAS_MISC, 0x00 }, + { WCD939X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD939X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD939X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD939X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD939X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD939X_HPH_CNP_EN, 0x80 }, + { WCD939X_HPH_CNP_WG_CTL, 0x9a }, + { WCD939X_HPH_CNP_WG_TIME, 0x14 }, + { WCD939X_HPH_OCP_CTL, 0x28 }, + { WCD939X_HPH_AUTO_CHOP, 0x56 }, + { WCD939X_HPH_CHOP_CTL, 0x83 }, + { WCD939X_HPH_PA_CTL1, 0x46 }, + { WCD939X_HPH_PA_CTL2, 0x50 }, + { WCD939X_HPH_L_EN, 0x80 }, + { WCD939X_HPH_L_TEST, 0xe0 }, + { WCD939X_HPH_L_ATEST, 0x50 }, + { WCD939X_HPH_R_EN, 0x80 }, + { WCD939X_HPH_R_TEST, 0xe0 }, + { WCD939X_HPH_R_ATEST, 0x50 }, + { WCD939X_HPH_RDAC_CLK_CTL1, 0x80 }, + { WCD939X_HPH_RDAC_CLK_CTL2, 0x0b }, + { WCD939X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_UHQA_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_LP_CTL, 0x8e }, + { WCD939X_HPH_L_DAC_CTL, 0x20 }, + { WCD939X_HPH_R_DAC_CTL, 0x20 }, + { WCD939X_HPH_SURGE_COMP_SEL, 0x55 }, + { WCD939X_HPH_SURGE_EN, 0x19 }, + { WCD939X_HPH_SURGE_MISC1, 0xa0 }, + { WCD939X_EAR_EN, 0x22 }, + { WCD939X_EAR_PA_CON, 0x44 }, + { WCD939X_EAR_SP_CON, 0xdb }, + { WCD939X_EAR_DAC_CON, 0x80 }, + { WCD939X_EAR_CNP_FSM_CON, 0xb2 }, + { WCD939X_EAR_TEST_CTL, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_2, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_3, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_4, 0x44 }, + { WCD939X_ANA_NEW_PAGE, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD939X_SLEEP_CTL, 0x18 }, + { WCD939X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD939X_MBHC_NEW_CTL_1, 0x02 }, + { WCD939X_MBHC_NEW_CTL_2, 0x05 }, + { WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD939X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD939X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD939X_TX_NEW_CH12_MUX, 0x11 }, + { WCD939X_TX_NEW_CH34_MUX, 0x23 }, + { WCD939X_DIE_CRACK_DET_EN, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_VREF_CTL, 0x08 }, + { WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_MISC1, 0x32 }, + { WCD939X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD939X_HPH_NEW_INT_TIMER1, 0xfe }, + { WCD939X_HPH_NEW_INT_TIMER2, 0x02 }, + { WCD939X_HPH_NEW_INT_TIMER3, 0x4e }, + { WCD939X_HPH_NEW_INT_TIMER4, 0x54 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC2, 0x0b }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xa0 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xa0 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x64 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW, 0x47 }, + { WCD939X_EAR_INT_NEW_CHOPPER_CON, 0xa8 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD939X_EAR_INT_NEW_DYNAMIC_BIAS, 0x00 }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a }, + { WCD939X_DIE_CRACK_INT_DET_INT1, 0x02 }, + { WCD939X_DIE_CRACK_INT_DET_INT2, 0x60 }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1, 0x7f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0, 0x3f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M, 0x1f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M, 0x0f }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1, 0xd7 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0, 0xc8 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP, 0xc6 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1, 0x95 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0, 0x6a }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, 0x05 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0, 0xa5 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, 0x13 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1, 0x88 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP, 0x42 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L1, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L0, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_ULP, 0x77 }, + { WCD939X_DIGITAL_PAGE, 0x00 }, + { WCD939X_DIGITAL_SWR_TX_CLK_RATE, 0x00 }, + { WCD939X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD939X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_DIG_CLK_CTL, 0xf0 }, + { WCD939X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD939X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD939X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_RX0_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX1_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX2_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00 }, + { WCD939X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1e }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R1, 0x4d }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R2, 0x29 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R3, 0x34 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R4, 0x59 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R5, 0x66 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R6, 0x87 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R7, 0x64 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_0, 0x96 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_1, 0x09 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_0, 0xab }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_1, 0x05 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_0, 0x1c }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_0, 0x17 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A6_0, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A7_0, 0xe3 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_0, 0x69 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_1, 0x54 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_2, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_3, 0x15 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R1, 0xa4 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R2, 0xb5 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R3, 0x86 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R4, 0x85 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R5, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R6, 0xe2 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R7, 0x62 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1, 0xfc }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD939X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD939X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_AMIC_CTL, 0x0f }, + { WCD939X_DIGITAL_CDC_DMIC_CTL, 0x04 }, + { WCD939X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC3_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC4_CTL, 0x01 }, + { WCD939X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD939X_DIGITAL_EFUSE_CTL, 0x2b }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11 }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11 }, + { WCD939X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD939X_DIGITAL_INTR_MODE, 0x00 }, + { WCD939X_DIGITAL_INTR_MASK_0, 0xff }, + { WCD939X_DIGITAL_INTR_MASK_1, 0xe7 }, + { WCD939X_DIGITAL_INTR_MASK_2, 0x0e }, + { WCD939X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_EN, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_0_1, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_2_3, 0x00 }, + { WCD939X_DIGITAL_LB_IN_SEL_CTL, 0x00 }, + { WCD939X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD939X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_2, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_SWR_0, 0x8f }, + { WCD939X_DIGITAL_PAD_CTL_SWR_1, 0x06 }, + { WCD939X_DIGITAL_I2C_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX2, 0xf1 }, + { WCD939X_DIGITAL_PAD_INP_DIS_0, 0x00 }, + { WCD939X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD939X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f }, + { WCD939X_DIGITAL_TX_DATA_EDGE_CTL, 0x80 }, + { WCD939X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD939X_DIGITAL_SSP_DBG, 0x00 }, + { WCD939X_DIGITAL_SPARE_0, 0x00 }, + { WCD939X_DIGITAL_SPARE_1, 0x00 }, + { WCD939X_DIGITAL_SPARE_2, 0x00 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_0, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_1, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_2, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_3, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_4, 0x88 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD939X_DIGITAL_DEM_SECOND_ORDER, 0x03 }, + { WCD939X_DIGITAL_DSM_CTRL, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_3, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_3, 0x00 }, + { WCD939X_RX_TOP_PAGE, 0x00 }, + { WCD939X_RX_TOP_TOP_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG1, 0x05 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG1, 0x03 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_PATH_CFG2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC4, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL0, 0x60 }, + { WCD939X_COMPANDER_HPHL_CTL1, 0xdb }, + { WCD939X_COMPANDER_HPHL_CTL2, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL3, 0x35 }, + { WCD939X_COMPANDER_HPHL_CTL4, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL7, 0x08 }, + { WCD939X_COMPANDER_HPHL_CTL8, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL9, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL10, 0x06 }, + { WCD939X_COMPANDER_HPHL_CTL11, 0x12 }, + { WCD939X_COMPANDER_HPHL_CTL12, 0x1e }, + { WCD939X_COMPANDER_HPHL_CTL13, 0x2a }, + { WCD939X_COMPANDER_HPHL_CTL14, 0x36 }, + { WCD939X_COMPANDER_HPHL_CTL15, 0x3c }, + { WCD939X_COMPANDER_HPHL_CTL16, 0xc4 }, + { WCD939X_COMPANDER_HPHL_CTL17, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL18, 0x0c }, + { WCD939X_COMPANDER_HPHL_CTL19, 0x16 }, + { WCD939X_R_CTL0, 0x60 }, + { WCD939X_R_CTL1, 0xdb }, + { WCD939X_R_CTL2, 0xff }, + { WCD939X_R_CTL3, 0x35 }, + { WCD939X_R_CTL4, 0xff }, + { WCD939X_R_CTL5, 0x00 }, + { WCD939X_R_CTL7, 0x08 }, + { WCD939X_R_CTL8, 0x00 }, + { WCD939X_R_CTL9, 0x00 }, + { WCD939X_R_CTL10, 0x06 }, + { WCD939X_R_CTL11, 0x12 }, + { WCD939X_R_CTL12, 0x1e }, + { WCD939X_R_CTL13, 0x2a }, + { WCD939X_R_CTL14, 0x36 }, + { WCD939X_R_CTL15, 0x3c }, + { WCD939X_R_CTL16, 0xc4 }, + { WCD939X_R_CTL17, 0x00 }, + { WCD939X_R_CTL18, 0x0c }, + { WCD939X_R_CTL19, 0x16 }, + { WCD939X_E_PATH_CTL, 0x00 }, + { WCD939X_E_CFG0, 0x07 }, + { WCD939X_E_CFG1, 0x3c }, + { WCD939X_E_CFG2, 0x00 }, + { WCD939X_E_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHL_CFG0, 0x00 }, + { WCD939X_DSD_HPHL_CFG1, 0x00 }, + { WCD939X_DSD_HPHL_CFG2, 0x22 }, + { WCD939X_DSD_HPHL_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_CFG4, 0x1a }, + { WCD939X_DSD_HPHL_CFG5, 0x00 }, + { WCD939X_DSD_HPHR_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHR_CFG0, 0x00 }, + { WCD939X_DSD_HPHR_CFG1, 0x00 }, + { WCD939X_DSD_HPHR_CFG2, 0x22 }, + { WCD939X_DSD_HPHR_CFG3, 0x00 }, + { WCD939X_DSD_HPHR_CFG4, 0x1a }, + { WCD939X_DSD_HPHR_CFG5, 0x00 }, +}; + +static bool wcd939x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_PAGE: + case WCD939X_ANA_BIAS: + case WCD939X_ANA_RX_SUPPLIES: + case WCD939X_ANA_HPH: + case WCD939X_ANA_EAR: + case WCD939X_ANA_EAR_COMPANDER_CTL: + case WCD939X_ANA_TX_CH1: + case WCD939X_ANA_TX_CH2: + case WCD939X_ANA_TX_CH3: + case WCD939X_ANA_TX_CH4: + case WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD939X_ANA_MICB3_DSP_EN_LOGIC: + case WCD939X_ANA_MBHC_MECH: + case WCD939X_ANA_MBHC_ELECT: + case WCD939X_ANA_MBHC_ZDET: + case WCD939X_ANA_MBHC_BTN0: + case WCD939X_ANA_MBHC_BTN1: + case WCD939X_ANA_MBHC_BTN2: + case WCD939X_ANA_MBHC_BTN3: + case WCD939X_ANA_MBHC_BTN4: + case WCD939X_ANA_MBHC_BTN5: + case WCD939X_ANA_MBHC_BTN6: + case WCD939X_ANA_MBHC_BTN7: + case WCD939X_ANA_MICB1: + case WCD939X_ANA_MICB2: + case WCD939X_ANA_MICB2_RAMP: + case WCD939X_ANA_MICB3: + case WCD939X_ANA_MICB4: + case WCD939X_BIAS_CTL: + case WCD939X_BIAS_VBG_FINE_ADJ: + case WCD939X_LDOL_VDDCX_ADJUST: + case WCD939X_LDOL_DISABLE_LDOL: + case WCD939X_MBHC_CTL_CLK: + case WCD939X_MBHC_CTL_ANA: + case WCD939X_MBHC_ZDET_VNEG_CTL: + case WCD939X_MBHC_ZDET_BIAS_CTL: + case WCD939X_MBHC_CTL_BCS: + case WCD939X_MBHC_TEST_CTL: + case WCD939X_LDOH_MODE: + case WCD939X_LDOH_BIAS: + case WCD939X_LDOH_STB_LOADS: + case WCD939X_LDOH_SLOWRAMP: + case WCD939X_MICB1_TEST_CTL_1: + case WCD939X_MICB1_TEST_CTL_2: + case WCD939X_MICB1_TEST_CTL_3: + case WCD939X_MICB2_TEST_CTL_1: + case WCD939X_MICB2_TEST_CTL_2: + case WCD939X_MICB2_TEST_CTL_3: + case WCD939X_MICB3_TEST_CTL_1: + case WCD939X_MICB3_TEST_CTL_2: + case WCD939X_MICB3_TEST_CTL_3: + case WCD939X_MICB4_TEST_CTL_1: + case WCD939X_MICB4_TEST_CTL_2: + case WCD939X_MICB4_TEST_CTL_3: + case WCD939X_TX_COM_ADC_VCM: + case WCD939X_TX_COM_BIAS_ATEST: + case WCD939X_TX_COM_SPARE1: + case WCD939X_TX_COM_SPARE2: + case WCD939X_TX_COM_TXFE_DIV_CTL: + case WCD939X_TX_COM_TXFE_DIV_START: + case WCD939X_TX_COM_SPARE3: + case WCD939X_TX_COM_SPARE4: + case WCD939X_TX_1_2_TEST_EN: + case WCD939X_TX_1_2_ADC_IB: + case WCD939X_TX_1_2_ATEST_REFCTL: + case WCD939X_TX_1_2_TEST_CTL: + case WCD939X_TX_1_2_TEST_BLK_EN1: + case WCD939X_TX_1_2_TXFE1_CLKDIV: + case WCD939X_TX_3_4_TEST_EN: + case WCD939X_TX_3_4_ADC_IB: + case WCD939X_TX_3_4_ATEST_REFCTL: + case WCD939X_TX_3_4_TEST_CTL: + case WCD939X_TX_3_4_TEST_BLK_EN3: + case WCD939X_TX_3_4_TXFE3_CLKDIV: + case WCD939X_TX_3_4_TEST_BLK_EN2: + case WCD939X_TX_3_4_TXFE2_CLKDIV: + case WCD939X_TX_3_4_SPARE1: + case WCD939X_TX_3_4_TEST_BLK_EN4: + case WCD939X_TX_3_4_TXFE4_CLKDIV: + case WCD939X_TX_3_4_SPARE2: + case WCD939X_CLASSH_MODE_1: + case WCD939X_CLASSH_MODE_2: + case WCD939X_CLASSH_MODE_3: + case WCD939X_CLASSH_CTRL_VCL_1: + case WCD939X_CLASSH_CTRL_VCL_2: + case WCD939X_CLASSH_CTRL_CCL_1: + case WCD939X_CLASSH_CTRL_CCL_2: + case WCD939X_CLASSH_CTRL_CCL_3: + case WCD939X_CLASSH_CTRL_CCL_4: + case WCD939X_CLASSH_CTRL_CCL_5: + case WCD939X_CLASSH_BUCK_TMUX_A_D: + case WCD939X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD939X_CLASSH_SPARE: + case WCD939X_FLYBACK_EN: + case WCD939X_FLYBACK_VNEG_CTRL_1: + case WCD939X_FLYBACK_VNEG_CTRL_2: + case WCD939X_FLYBACK_VNEG_CTRL_3: + case WCD939X_FLYBACK_VNEG_CTRL_4: + case WCD939X_FLYBACK_VNEG_CTRL_5: + case WCD939X_FLYBACK_VNEG_CTRL_6: + case WCD939X_FLYBACK_VNEG_CTRL_7: + case WCD939X_FLYBACK_VNEG_CTRL_8: + case WCD939X_FLYBACK_VNEG_CTRL_9: + case WCD939X_FLYBACK_VNEGDAC_CTRL_1: + case WCD939X_FLYBACK_VNEGDAC_CTRL_2: + case WCD939X_FLYBACK_VNEGDAC_CTRL_3: + case WCD939X_FLYBACK_CTRL_1: + case WCD939X_FLYBACK_TEST_CTL: + case WCD939X_RX_AUX_SW_CTL: + case WCD939X_RX_PA_AUX_IN_CONN: + case WCD939X_RX_TIMER_DIV: + case WCD939X_RX_OCP_CTL: + case WCD939X_RX_OCP_COUNT: + case WCD939X_RX_BIAS_EAR_DAC: + case WCD939X_RX_BIAS_EAR_AMP: + case WCD939X_RX_BIAS_HPH_LDO: + case WCD939X_RX_BIAS_HPH_PA: + case WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD939X_RX_BIAS_HPH_RDAC_LDO: + case WCD939X_RX_BIAS_HPH_CNP1: + case WCD939X_RX_BIAS_HPH_LOWPOWER: + case WCD939X_RX_BIAS_AUX_DAC: + case WCD939X_RX_BIAS_AUX_AMP: + case WCD939X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD939X_RX_BIAS_MISC: + case WCD939X_RX_BIAS_BUCK_RST: + case WCD939X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD939X_RX_BIAS_FLYB_ERRAMP: + case WCD939X_RX_BIAS_FLYB_BUFF: + case WCD939X_RX_BIAS_FLYB_MID_RST: + case WCD939X_HPH_CNP_EN: + case WCD939X_HPH_CNP_WG_CTL: + case WCD939X_HPH_CNP_WG_TIME: + case WCD939X_HPH_OCP_CTL: + case WCD939X_HPH_AUTO_CHOP: + case WCD939X_HPH_CHOP_CTL: + case WCD939X_HPH_PA_CTL1: + case WCD939X_HPH_PA_CTL2: + case WCD939X_HPH_L_EN: + case WCD939X_HPH_L_TEST: + case WCD939X_HPH_L_ATEST: + case WCD939X_HPH_R_EN: + case WCD939X_HPH_R_TEST: + case WCD939X_HPH_R_ATEST: + case WCD939X_HPH_RDAC_CLK_CTL1: + case WCD939X_HPH_RDAC_CLK_CTL2: + case WCD939X_HPH_RDAC_LDO_CTL: + case WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD939X_HPH_REFBUFF_UHQA_CTL: + case WCD939X_HPH_REFBUFF_LP_CTL: + case WCD939X_HPH_L_DAC_CTL: + case WCD939X_HPH_R_DAC_CTL: + case WCD939X_HPH_SURGE_COMP_SEL: + case WCD939X_HPH_SURGE_EN: + case WCD939X_HPH_SURGE_MISC1: + case WCD939X_EAR_EN: + case WCD939X_EAR_PA_CON: + case WCD939X_EAR_SP_CON: + case WCD939X_EAR_DAC_CON: + case WCD939X_EAR_CNP_FSM_CON: + case WCD939X_EAR_TEST_CTL: + case WCD939X_FLYBACK_NEW_CTRL_2: + case WCD939X_FLYBACK_NEW_CTRL_3: + case WCD939X_FLYBACK_NEW_CTRL_4: + case WCD939X_ANA_NEW_PAGE: + case WCD939X_HPH_NEW_ANA_HPH2: + case WCD939X_HPH_NEW_ANA_HPH3: + case WCD939X_SLEEP_CTL: + case WCD939X_SLEEP_WATCHDOG_CTL: + case WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD939X_MBHC_NEW_CTL_1: + case WCD939X_MBHC_NEW_CTL_2: + case WCD939X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD939X_MBHC_NEW_ZDET_ANA_CTL: + case WCD939X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD939X_TX_NEW_CH12_MUX: + case WCD939X_TX_NEW_CH34_MUX: + case WCD939X_DIE_CRACK_DET_EN: + case WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R: + case WCD939X_HPH_NEW_INT_PA_MISC1: + case WCD939X_HPH_NEW_INT_PA_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD939X_HPH_NEW_INT_TIMER1: + case WCD939X_HPH_NEW_INT_TIMER2: + case WCD939X_HPH_NEW_INT_TIMER3: + case WCD939X_HPH_NEW_INT_TIMER4: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW: + case WCD939X_EAR_INT_NEW_CHOPPER_CON: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD939X_EAR_INT_NEW_DYNAMIC_BIAS: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD939X_DIE_CRACK_INT_DET_INT1: + case WCD939X_DIE_CRACK_INT_DET_INT2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L2: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L1: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L0: + case WCD939X_TX_COM_NEW_INT_ADC_INT_ULP: + case WCD939X_DIGITAL_PAGE: + case WCD939X_DIGITAL_SWR_TX_CLK_RATE: + case WCD939X_DIGITAL_CDC_RST_CTL: + case WCD939X_DIGITAL_TOP_CLK_CFG: + case WCD939X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD939X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD939X_DIGITAL_SWR_RST_EN: + case WCD939X_DIGITAL_CDC_PATH_MODE: + case WCD939X_DIGITAL_CDC_RX_RST: + case WCD939X_DIGITAL_CDC_RX0_CTL: + case WCD939X_DIGITAL_CDC_RX1_CTL: + case WCD939X_DIGITAL_CDC_RX2_CTL: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3: + case WCD939X_DIGITAL_CDC_COMP_CTL_0: + case WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R1: + case WCD939X_DIGITAL_CDC_HPH_DSM_R2: + case WCD939X_DIGITAL_CDC_HPH_DSM_R3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R4: + case WCD939X_DIGITAL_CDC_HPH_DSM_R5: + case WCD939X_DIGITAL_CDC_HPH_DSM_R6: + case WCD939X_DIGITAL_CDC_HPH_DSM_R7: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A6_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A7_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_2: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R1: + case WCD939X_DIGITAL_CDC_EAR_DSM_R2: + case WCD939X_DIGITAL_CDC_EAR_DSM_R3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R4: + case WCD939X_DIGITAL_CDC_EAR_DSM_R5: + case WCD939X_DIGITAL_CDC_EAR_DSM_R6: + case WCD939X_DIGITAL_CDC_EAR_DSM_R7: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD939X_DIGITAL_CDC_SWR_CLH: + case WCD939X_DIGITAL_SWR_CLH_BYP: + case WCD939X_DIGITAL_CDC_TX0_CTL: + case WCD939X_DIGITAL_CDC_TX1_CTL: + case WCD939X_DIGITAL_CDC_TX2_CTL: + case WCD939X_DIGITAL_CDC_TX_RST: + case WCD939X_DIGITAL_CDC_REQ_CTL: + case WCD939X_DIGITAL_CDC_RST: + case WCD939X_DIGITAL_CDC_AMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC1_CTL: + case WCD939X_DIGITAL_CDC_DMIC2_CTL: + case WCD939X_DIGITAL_CDC_DMIC3_CTL: + case WCD939X_DIGITAL_CDC_DMIC4_CTL: + case WCD939X_DIGITAL_EFUSE_PRG_CTL: + case WCD939X_DIGITAL_EFUSE_CTL: + case WCD939X_DIGITAL_CDC_DMIC_RATE_1_2: + case WCD939X_DIGITAL_CDC_DMIC_RATE_3_4: + case WCD939X_DIGITAL_PDM_WD_CTL0: + case WCD939X_DIGITAL_PDM_WD_CTL1: + case WCD939X_DIGITAL_PDM_WD_CTL2: + case WCD939X_DIGITAL_INTR_MODE: + case WCD939X_DIGITAL_INTR_MASK_0: + case WCD939X_DIGITAL_INTR_MASK_1: + case WCD939X_DIGITAL_INTR_MASK_2: + case WCD939X_DIGITAL_INTR_CLEAR_0: + case WCD939X_DIGITAL_INTR_CLEAR_1: + case WCD939X_DIGITAL_INTR_CLEAR_2: + case WCD939X_DIGITAL_INTR_LEVEL_0: + case WCD939X_DIGITAL_INTR_LEVEL_1: + case WCD939X_DIGITAL_INTR_LEVEL_2: + case WCD939X_DIGITAL_INTR_SET_0: + case WCD939X_DIGITAL_INTR_SET_1: + case WCD939X_DIGITAL_INTR_SET_2: + case WCD939X_DIGITAL_INTR_TEST_0: + case WCD939X_DIGITAL_INTR_TEST_1: + case WCD939X_DIGITAL_INTR_TEST_2: + case WCD939X_DIGITAL_TX_MODE_DBG_EN: + case WCD939X_DIGITAL_TX_MODE_DBG_0_1: + case WCD939X_DIGITAL_TX_MODE_DBG_2_3: + case WCD939X_DIGITAL_LB_IN_SEL_CTL: + case WCD939X_DIGITAL_LOOP_BACK_MODE: + case WCD939X_DIGITAL_SWR_DAC_TEST: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_2: + case WCD939X_DIGITAL_PAD_CTL_SWR_0: + case WCD939X_DIGITAL_PAD_CTL_SWR_1: + case WCD939X_DIGITAL_I2C_CTL: + case WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX2: + case WCD939X_DIGITAL_PAD_INP_DIS_0: + case WCD939X_DIGITAL_PAD_INP_DIS_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_0: + case WCD939X_DIGITAL_DRIVE_STRENGTH_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_2: + case WCD939X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_GPIO_MODE: + case WCD939X_DIGITAL_PIN_CTL_OE: + case WCD939X_DIGITAL_PIN_CTL_DATA_0: + case WCD939X_DIGITAL_PIN_CTL_DATA_1: + case WCD939X_DIGITAL_DIG_DEBUG_CTL: + case WCD939X_DIGITAL_DIG_DEBUG_EN: + case WCD939X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD939X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD939X_DIGITAL_SSP_DBG: + case WCD939X_DIGITAL_SPARE_0: + case WCD939X_DIGITAL_SPARE_1: + case WCD939X_DIGITAL_SPARE_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_0: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_1: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_3: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_4: + case WCD939X_DIGITAL_DEM_BYPASS_DATA0: + case WCD939X_DIGITAL_DEM_BYPASS_DATA1: + case WCD939X_DIGITAL_DEM_BYPASS_DATA2: + case WCD939X_DIGITAL_DEM_BYPASS_DATA3: + case WCD939X_DIGITAL_DEM_SECOND_ORDER: + case WCD939X_DIGITAL_DSM_CTRL: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_3: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_3: + case WCD939X_RX_TOP_PAGE: + case WCD939X_RX_TOP_TOP_CFG0: + case WCD939X_RX_TOP_HPHL_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHL_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHL_COMP_LUT: + case WCD939X_RX_TOP_HPHR_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHR_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHR_COMP_LUT: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG4: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG4: + case WCD939X_RX_TOP_HPHL_PATH_CFG0: + case WCD939X_RX_TOP_HPHL_PATH_CFG1: + case WCD939X_RX_TOP_HPHR_PATH_CFG0: + case WCD939X_RX_TOP_HPHR_PATH_CFG1: + case WCD939X_RX_TOP_PATH_CFG2: + case WCD939X_RX_TOP_HPHL_PATH_SEC0: + case WCD939X_RX_TOP_HPHL_PATH_SEC1: + case WCD939X_RX_TOP_HPHL_PATH_SEC2: + case WCD939X_RX_TOP_HPHL_PATH_SEC3: + case WCD939X_RX_TOP_HPHR_PATH_SEC0: + case WCD939X_RX_TOP_HPHR_PATH_SEC1: + case WCD939X_RX_TOP_HPHR_PATH_SEC2: + case WCD939X_RX_TOP_HPHR_PATH_SEC3: + case WCD939X_RX_TOP_PATH_SEC4: + case WCD939X_RX_TOP_PATH_SEC5: + case WCD939X_COMPANDER_HPHL_CTL0: + case WCD939X_COMPANDER_HPHL_CTL1: + case WCD939X_COMPANDER_HPHL_CTL2: + case WCD939X_COMPANDER_HPHL_CTL3: + case WCD939X_COMPANDER_HPHL_CTL4: + case WCD939X_COMPANDER_HPHL_CTL5: + case WCD939X_COMPANDER_HPHL_CTL7: + case WCD939X_COMPANDER_HPHL_CTL8: + case WCD939X_COMPANDER_HPHL_CTL9: + case WCD939X_COMPANDER_HPHL_CTL10: + case WCD939X_COMPANDER_HPHL_CTL11: + case WCD939X_COMPANDER_HPHL_CTL12: + case WCD939X_COMPANDER_HPHL_CTL13: + case WCD939X_COMPANDER_HPHL_CTL14: + case WCD939X_COMPANDER_HPHL_CTL15: + case WCD939X_COMPANDER_HPHL_CTL16: + case WCD939X_COMPANDER_HPHL_CTL17: + case WCD939X_COMPANDER_HPHL_CTL18: + case WCD939X_COMPANDER_HPHL_CTL19: + case WCD939X_R_CTL0: + case WCD939X_R_CTL1: + case WCD939X_R_CTL2: + case WCD939X_R_CTL3: + case WCD939X_R_CTL4: + case WCD939X_R_CTL5: + case WCD939X_R_CTL7: + case WCD939X_R_CTL8: + case WCD939X_R_CTL9: + case WCD939X_R_CTL10: + case WCD939X_R_CTL11: + case WCD939X_R_CTL12: + case WCD939X_R_CTL13: + case WCD939X_R_CTL14: + case WCD939X_R_CTL15: + case WCD939X_R_CTL16: + case WCD939X_R_CTL17: + case WCD939X_R_CTL18: + case WCD939X_R_CTL19: + case WCD939X_E_PATH_CTL: + case WCD939X_E_CFG0: + case WCD939X_E_CFG1: + case WCD939X_E_CFG2: + case WCD939X_E_CFG3: + case WCD939X_DSD_HPHL_PATH_CTL: + case WCD939X_DSD_HPHL_CFG0: + case WCD939X_DSD_HPHL_CFG1: + case WCD939X_DSD_HPHL_CFG2: + case WCD939X_DSD_HPHL_CFG3: + case WCD939X_DSD_HPHL_CFG4: + case WCD939X_DSD_HPHL_CFG5: + case WCD939X_DSD_HPHR_PATH_CTL: + case WCD939X_DSD_HPHR_CFG0: + case WCD939X_DSD_HPHR_CFG1: + case WCD939X_DSD_HPHR_CFG2: + case WCD939X_DSD_HPHR_CFG3: + case WCD939X_DSD_HPHR_CFG4: + case WCD939X_DSD_HPHR_CFG5: + return true; + } + + return false; +} + +static bool wcd939x_readable_register(struct device *dev, unsigned int reg) +{ + /* Read-Only Registers */ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_CHIP_ID0: + case WCD939X_DIGITAL_CHIP_ID1: + case WCD939X_DIGITAL_CHIP_ID2: + case WCD939X_DIGITAL_CHIP_ID3: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_EFUSE_T_DATA_0: + case WCD939X_DIGITAL_EFUSE_T_DATA_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_DIGITAL_EFUSE_REG_0: + case WCD939X_DIGITAL_EFUSE_REG_1: + case WCD939X_DIGITAL_EFUSE_REG_2: + case WCD939X_DIGITAL_EFUSE_REG_3: + case WCD939X_DIGITAL_EFUSE_REG_4: + case WCD939X_DIGITAL_EFUSE_REG_5: + case WCD939X_DIGITAL_EFUSE_REG_6: + case WCD939X_DIGITAL_EFUSE_REG_7: + case WCD939X_DIGITAL_EFUSE_REG_8: + case WCD939X_DIGITAL_EFUSE_REG_9: + case WCD939X_DIGITAL_EFUSE_REG_10: + case WCD939X_DIGITAL_EFUSE_REG_11: + case WCD939X_DIGITAL_EFUSE_REG_12: + case WCD939X_DIGITAL_EFUSE_REG_13: + case WCD939X_DIGITAL_EFUSE_REG_14: + case WCD939X_DIGITAL_EFUSE_REG_15: + case WCD939X_DIGITAL_EFUSE_REG_16: + case WCD939X_DIGITAL_EFUSE_REG_17: + case WCD939X_DIGITAL_EFUSE_REG_18: + case WCD939X_DIGITAL_EFUSE_REG_19: + case WCD939X_DIGITAL_EFUSE_REG_20: + case WCD939X_DIGITAL_EFUSE_REG_21: + case WCD939X_DIGITAL_EFUSE_REG_22: + case WCD939X_DIGITAL_EFUSE_REG_23: + case WCD939X_DIGITAL_EFUSE_REG_24: + case WCD939X_DIGITAL_EFUSE_REG_25: + case WCD939X_DIGITAL_EFUSE_REG_26: + case WCD939X_DIGITAL_EFUSE_REG_27: + case WCD939X_DIGITAL_EFUSE_REG_28: + case WCD939X_DIGITAL_EFUSE_REG_29: + case WCD939X_DIGITAL_EFUSE_REG_30: + case WCD939X_DIGITAL_EFUSE_REG_31: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + + return wcd939x_rdwr_register(dev, reg); +} + +static bool wcd939x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + return false; +} + +static bool wcd939x_writeable_register(struct device *dev, unsigned int reg) +{ + return wcd939x_rdwr_register(dev, reg); +} + +static const struct regmap_config wcd939x_regmap_config = { + .name = "wcd939x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd939x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd939x_defaults), + .max_register = WCD939X_MAX_REGISTER, + .readable_reg = wcd939x_readable_register, + .writeable_reg = wcd939x_writeable_register, + .volatile_reg = wcd939x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9390_slave_ops = { + .update_status = wcd9390_update_status, + .interrupt_callback = wcd9390_interrupt_callback, + .bus_config = wcd9390_bus_config, +}; + +static int wcd939x_sdw_component_bind(struct device *dev, struct device *master, + void *data) +{ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static void wcd939x_sdw_component_unbind(struct device *dev, + struct device *master, void *data) +{ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); +} + +static const struct component_ops wcd939x_sdw_component_ops = { + .bind = wcd939x_sdw_component_bind, + .unbind = wcd939x_sdw_component_unbind, +}; + +static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /* + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + wcd->is_tx = true; + ret = of_property_read_u32_array(dev->of_node, + "qcom,tx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, + "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_RX_SWR_PORTS); + } + + if (ret < 0) + dev_info(dev, "Static Port mapping not specified\n"); + + wcd->sdev = pdev; + dev_set_drvdata(dev, wcd); + + pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | + SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD939X_MAX_TX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd939x_tx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + } else { + pdev->prop.sink_ports = GENMASK(WCD939X_MAX_RX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd939x_rx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_rx_ch_info[0]; + } + + if (wcd->is_tx) { + /* + * Do not use devres here since devres_release_group() could + * be called by component_unbind() id the aggregate device + * fails to bind. + */ + wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Regmap init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wcd->regmap, true); + } + + ret = component_add(dev, &wcd939x_sdw_component_ops); + if (ret) + return ret; + + /* Set suspended until aggregate device is bind */ + pm_runtime_set_suspended(dev); + + return 0; +} + +static int wcd9390_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + component_del(dev, &wcd939x_sdw_component_ops); + + if (wcd->regmap) + regmap_exit(wcd->regmap); + + return 0; +} + +static const struct sdw_device_id wcd9390_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10e, 0), /* WCD9390 & WCD9390 RX/TX Device ID */ + {}, +}; +MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id); + +static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + } + + return 0; +} + +static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); + regcache_sync(wcd->regmap); + } + + return 0; +} + +static const struct dev_pm_ops wcd939x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL) +}; + +static struct sdw_driver wcd9390_codec_driver = { + .probe = wcd9390_probe, + .remove = wcd9390_remove, + .ops = &wcd9390_slave_ops, + .id_table = wcd9390_slave_id, + .driver = { + .name = "wcd9390-codec", + .pm = &wcd939x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9390_codec_driver); + +MODULE_DESCRIPTION("WCD939X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c new file mode 100644 index 000000000000..c49894aad8a5 --- /dev/null +++ b/sound/soc/codecs/wcd939x.c @@ -0,0 +1,3686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/pm_runtime.h> +#include <linux/component.h> +#include <sound/tlv.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/of.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/regulator/consumer.h> +#include <linux/usb/typec_mux.h> +#include <linux/usb/typec_altmode.h> + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#include "wcd939x.h" + +#define WCD939X_MAX_MICBIAS (4) +#define WCD939X_MAX_SUPPLY (4) +#define WCD939X_MBHC_MAX_BUTTONS (8) +#define TX_ADC_MAX (4) +#define WCD_MBHC_HS_V_MAX 1600 + +enum { + WCD939X_VERSION_1_0 = 0, + WCD939X_VERSION_1_1, + WCD939X_VERSION_2_0, +}; + +#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) +#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) +#define SWR_CLK_RATE_0P6MHZ (600000) +#define SWR_CLK_RATE_1P2MHZ (1200000) +#define SWR_CLK_RATE_2P4MHZ (2400000) +#define SWR_CLK_RATE_4P8MHZ (4800000) +#define SWR_CLK_RATE_9P6MHZ (9600000) +#define SWR_CLK_RATE_11P2896MHZ (1128960) + +#define ADC_MODE_VAL_HIFI 0x01 +#define ADC_MODE_VAL_LO_HIF 0x02 +#define ADC_MODE_VAL_NORMAL 0x03 +#define ADC_MODE_VAL_LP 0x05 +#define ADC_MODE_VAL_ULP1 0x09 +#define ADC_MODE_VAL_ULP2 0x0B + +/* Z value defined in milliohm */ +#define WCD939X_ZDET_VAL_32 (32000) +#define WCD939X_ZDET_VAL_400 (400000) +#define WCD939X_ZDET_VAL_1200 (1200000) +#define WCD939X_ZDET_VAL_100K (100000000) + +/* Z floating defined in ohms */ +#define WCD939X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD939X_ZDET_NUM_MEASUREMENTS (900) +#define WCD939X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14) +#define WCD939X_MBHC_GET_X1(x) ((x) & 0x3FFF) + +/* Z value compared in milliOhm */ +#define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false +#define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024) + +enum { + WCD9390 = 0, + WCD9395 = 5, +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD939X_IRQ_MBHC_SW_DET, + WCD939X_IRQ_HPHR_OCP_INT, + WCD939X_IRQ_HPHR_CNP_INT, + WCD939X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD939X_IRQ_HPHL_CNP_INT, + WCD939X_IRQ_EAR_CNP_INT, + WCD939X_IRQ_EAR_SCD_INT, + WCD939X_IRQ_HPHL_PDM_WD_INT, + WCD939X_IRQ_HPHR_PDM_WD_INT, + WCD939X_IRQ_EAR_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD939X_IRQ_MBHC_MOISTURE_INT, + WCD939X_IRQ_HPHL_SURGE_DET_INT, + WCD939X_IRQ_HPHR_SURGE_DET_INT, + WCD939X_NUM_IRQS, +}; + +enum { + MICB_BIAS_DISABLE = 0, + MICB_BIAS_ENABLE, + MICB_BIAS_PULL_UP, + MICB_BIAS_PULL_DOWN, +}; + +enum { + WCD_ADC1 = 0, + WCD_ADC2, + WCD_ADC3, + WCD_ADC4, + HPH_PA_DELAY, +}; + +enum { + ADC_MODE_INVALID = 0, + ADC_MODE_HIFI, + ADC_MODE_LO_HIF, + ADC_MODE_NORMAL, + ADC_MODE_LP, + ADC_MODE_ULP1, + ADC_MODE_ULP2, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +static u8 tx_mode_bit[] = { + [ADC_MODE_INVALID] = 0x00, + [ADC_MODE_HIFI] = 0x01, + [ADC_MODE_LO_HIF] = 0x02, + [ADC_MODE_NORMAL] = 0x04, + [ADC_MODE_LP] = 0x08, + [ADC_MODE_ULP1] = 0x10, + [ADC_MODE_ULP2] = 0x20, +}; + +struct zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +struct wcd939x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode, *txnode; + struct regmap *regmap; + struct snd_soc_component *component; + /* micb setup lock */ + struct mutex micb_lock; + /* typec handling */ + bool typec_analog_mux; +#if IS_ENABLED(CONFIG_TYPEC) + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; + enum typec_orientation typec_orientation; + unsigned long typec_mode; + struct typec_switch *typec_switch; +#endif /* CONFIG_TYPEC */ + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; + struct wcd_clsh_ctrl *clsh_info; + struct irq_domain *virq; + struct regmap_irq_chip *wcd_regmap_irq_chip; + struct regmap_irq_chip_data *irq_chip; + struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY]; + struct snd_soc_jack *jack; + unsigned long status_mask; + s32 micb_ref[WCD939X_MAX_MICBIAS]; + s32 pullup_ref[WCD939X_MAX_MICBIAS]; + u32 hph_mode; + u32 tx_mode[TX_ADC_MAX]; + int variant; + int reset_gpio; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int ear_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + bool ldoh; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip wcd939x_regmap_irq_chip = { + .name = "wcd939x", + .irqs = wcd939x_irqs, + .num_irqs = ARRAY_SIZE(wcd939x_irqs), + .num_regs = 3, + .status_base = WCD939X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD939X_DIGITAL_INTR_MASK_0, + .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .runtime_pm = true, + .irq_drv_data = NULL, +}; + +static int wcd939x_get_clk_rate(int mode) +{ + int rate; + + switch (mode) { + case ADC_MODE_ULP2: + rate = SWR_CLK_RATE_0P6MHZ; + break; + case ADC_MODE_ULP1: + rate = SWR_CLK_RATE_1P2MHZ; + break; + case ADC_MODE_LP: + rate = SWR_CLK_RATE_4P8MHZ; + break; + case ADC_MODE_NORMAL: + case ADC_MODE_LO_HIF: + case ADC_MODE_HIFI: + case ADC_MODE_INVALID: + default: + rate = SWR_CLK_RATE_9P6MHZ; + break; + } + + return rate; +} + +static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank) +{ + u8 mask = (bank ? 0xF0 : 0x0F); + u8 val = 0; + + switch (rate) { + case SWR_CLK_RATE_0P6MHZ: + val = 6; + break; + case SWR_CLK_RATE_1P2MHZ: + val = 5; + break; + case SWR_CLK_RATE_2P4MHZ: + val = 3; + break; + case SWR_CLK_RATE_4P8MHZ: + val = 1; + break; + case SWR_CLK_RATE_9P6MHZ: + default: + val = 0; + break; + } + + snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val); + + return 0; +} + +static int wcd939x_io_init(struct snd_soc_component *component) +{ + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_ANALOG_BIAS_EN, true); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, true); + + /* 10 msec delay as per HW requirement */ + usleep_range(10000, 10010); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1); + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, + WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8); + + snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2, + WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_OCP_FSM_EN, true); + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_SCD_OP_EN, true); + + snd_soc_component_write(component, WCD939X_E_CFG0, + WCD939X_CFG0_IDLE_STEREO | + WCD939X_CFG0_AUTO_DISABLE_ANC); + + return 0; +} + +static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, + struct sdw_port_config *port_config, + u8 enable) +{ + u8 ch_mask, port_num; + + port_num = ch_info->port_num; + ch_mask = ch_info->ch_mask; + + port_config->num = port_num; + + if (enable) + port_config->ch_mask |= ch_mask; + else + port_config->ch_mask &= ~ch_mask; + + return 0; +} + +static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable) +{ + return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id], + &wcd->port_config[port_num - 1], + enable); +} + +static int wcd939x_codec_enable_rxclk(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_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true); + + /* Analog path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + true); + + /* Digital path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VNEG_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VPOS_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false); + + break; + } + + return 0; +} + +static int wcd939x_codec_hphl_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d); + if (wcd939x->comp1_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp2_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN)) + usleep_range(5000, 5010); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_L_EN, + WCD939X_L_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_hphr_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d); + if (wcd939x->comp2_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp1_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN)) + usleep_range(5000, 5010); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_R_EN, + WCD939X_R_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_ear_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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, + WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true); + + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false); + + /* 5 msec delay as per HW requirement */ + usleep_range(5000, 5010); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphr_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); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + + enable_irq(wcd939x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_HPHR_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHR_PA_OFF); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphl_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); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + enable_irq(wcd939x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(21000, 21100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_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); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable watchdog interrupt for HPHL */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + /* For EAR, use CLASS_AB regulator mode */ + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, true); + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 6 msec delay as per HW requirement */ + usleep_range(6000, 6010); + enable_irq(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, + false); + /* 7 msec delay as per HW requirement */ + usleep_range(7000, 7010); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + break; + } + + return 0; +} + +/* TX Controls */ + +static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 dmic_clk_reg, dmic_clk_en_reg; + u8 dmic_clk_en_mask; + u8 dmic_ctl_mask; + u8 dmic_clk_mask; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL; + break; + case 2: + case 3: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL; + break; + case 4: + case 5: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL; + break; + case 6: + case 7: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, false); + /* 250us sleep as per HW requirement */ + usleep_range(250, 260); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + true); + /* Setting DMIC clock rate to 2.4MHz */ + snd_soc_component_write_field(component, dmic_clk_reg, + dmic_clk_mask, 3); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, true); + /* enable clock scaling */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, 1); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + false); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, 0); + break; + } + return 0; +} + +static int wcd939x_tx_swr_ctrl(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int bank; + int rate; + + bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + int mode = 0; + + if (test_bit(WCD_ADC1, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]]; + if (test_bit(WCD_ADC2, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]]; + if (test_bit(WCD_ADC3, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]]; + if (test_bit(WCD_ADC4, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]]; + + if (mode) + rate = wcd939x_get_clk_rate(ffs(mode) - 1); + else + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, bank); + wcd939x_set_swr_clk_rate(component, rate, !bank); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, !bank); + wcd939x_set_swr_clk_rate(component, rate, bank); + } + break; + } + + return 0; +} + +static int wcd939x_get_adc_mode(int val) +{ + int ret = 0; + + switch (val) { + case ADC_MODE_INVALID: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_HIFI: + ret = ADC_MODE_VAL_HIFI; + break; + case ADC_MODE_LO_HIF: + ret = ADC_MODE_VAL_LO_HIF; + break; + case ADC_MODE_NORMAL: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_LP: + ret = ADC_MODE_VAL_LP; + break; + case ADC_MODE_ULP1: + ret = ADC_MODE_VAL_ULP1; + break; + case ADC_MODE_ULP2: + ret = ADC_MODE_VAL_ULP2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd939x_codec_enable_adc(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + set_bit(w->shift, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, + false); + clear_bit(w->shift, &wcd939x->status_mask); + break; + } + + return 0; +} + +static void wcd939x_tx_channel_config(struct snd_soc_component *component, + int channel, bool init) +{ + int reg, mask; + + switch (channel) { + case 0: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF1_INIT; + break; + case 1: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF2_INIT; + break; + case 2: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF3_INIT; + break; + case 3: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF4_INIT; + break; + default: + return; + } + + snd_soc_component_write_field(component, reg, mask, init); +} + +static int wcd939x_adc_enable_req(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 wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_NO_NOTCH, false); + + wcd939x_tx_channel_config(component, w->shift, true); + mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]); + if (mode < 0) { + dev_info(component->dev, "Invalid ADC mode\n"); + return -EINVAL; + } + + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + true); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + true); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + true); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + true); + break; + default: + break; + } + + wcd939x_tx_channel_config(component, w->shift, false); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + false); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + false); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + false); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + false); + break; + default: + break; + } + break; + } + + return 0; +} + +static int wcd939x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_field; + u16 micb_reg; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_field = WCD939X_MICB1_ENABLE; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_field = WCD939X_MICB2_ENABLE; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_field = WCD939X_MICB3_ENABLE; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_field = WCD939X_MICB4_ENABLE; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd939x->pullup_ref[micb_index]++; + if (wcd939x->pullup_ref[micb_index] == 1 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + break; + case MICB_PULLUP_DISABLE: + if (wcd939x->pullup_ref[micb_index] > 0) + wcd939x->pullup_ref[micb_index]--; + if (wcd939x->pullup_ref[micb_index] == 0 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + break; + case MICB_ENABLE: + wcd939x->micb_ref[micb_index]++; + if (wcd939x->micb_ref[micb_index] == 1) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, + WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_MICB1_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB2_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB3_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB4_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, micb_reg, micb_field, + MICB_BIAS_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + } + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + break; + case MICB_DISABLE: + if (wcd939x->micb_ref[micb_index] > 0) + wcd939x->micb_ref[micb_index]--; + + if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] > 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + else if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] == 0) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path]; + + return 0; +} + +static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0]; + + return 1; +} + +/* RX Controls */ + +static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->hph_mode; + + return 0; +} + +static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == wcd939x->hph_mode) + return 0; + + if (wcd939x->variant == WCD9390) { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } else { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_HIFI: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_HIFI: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } + + dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__); + return -EINVAL; +} + +static int wcd939x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (mc->shift) + ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0; + else + ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0; + + return 0; +} + +static int wcd939x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB]; + bool value = !!ucontrol->value.integer.value[0]; + int portidx = wcd->ch_info[mc->reg].port_num; + + if (mc->shift) + wcd939x->comp2_enable = value; + else + wcd939x->comp1_enable = value; + + if (value) + wcd939x_connect_port(wcd, portidx, mc->reg, true); + else + wcd939x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0; + + return 0; +} + +static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->ldoh == !!ucontrol->value.integer.value[0]) + return 0; + + wcd939x->ldoh = !!ucontrol->value.integer.value[0]; + + return 1; +} + +static const char * const tx_mode_mux_text_wcd9390[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", +}; + +static const struct soc_enum tx0_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx1_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx2_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx3_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const char * const tx_mode_mux_text[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", + "ADC_ULP1", "ADC_ULP2", +}; + +static const struct soc_enum tx0_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx1_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx2_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx3_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const char * const rx_hph_mode_mux_text_wcd9390[] = { + "CLS_H_NORMAL", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP", + "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390), + rx_hph_mode_mux_text_wcd9390); + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_NORMAL", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct snd_kcontrol_new wcd9390_snd_controls[] = { + SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new wcd9395_snd_controls[] = { + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic7_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic8_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const adc1_mux_text[] = { + "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4", "CH1_AMIC5" +}; + +static const struct soc_enum adc1_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0, + ARRAY_SIZE(adc1_mux_text), adc1_mux_text); + +static const struct snd_kcontrol_new tx_adc1_mux = + SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum); + +static const char * const adc2_mux_text[] = { + "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4", "CH2_AMIC5" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const char * const adc3_mux_text[] = { + "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4", "CH3_AMIC5" +}; + +static const struct soc_enum adc3_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0, + ARRAY_SIZE(adc3_mux_text), adc3_mux_text); + +static const struct snd_kcontrol_new tx_adc3_mux = + SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum); + +static const char * const adc4_mux_text[] = { + "CH4_AMIC_DISABLE", "CH4_AMIC1", "CH4_AMIC3", "CH4_AMIC4", "CH4_AMIC5" +}; + +static const struct soc_enum adc4_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3, + ARRAY_SIZE(adc4_mux_text), adc4_mux_text); + +static const struct snd_kcontrol_new tx_adc4_mux = + SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum); + +static const char * const rdac3_mux_text[] = { + "RX3", "RX1" +}; + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new rx_rdac3_mux = + SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0; + + return 0; +} + +static const char *version_to_str(u32 version) +{ + switch (version) { + case WCD939X_VERSION_1_0: + return __stringify(WCD939X_1_0); + case WCD939X_VERSION_1_1: + return __stringify(WCD939X_1_1); + case WCD939X_VERSION_2_0: + return __stringify(WCD939X_2_0); + } + return NULL; +} + +static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0]; + + wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]); + + return 1; +} + +/* MBHC Related */ + +static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1, + WCD939X_CTL_1_RCO_EN, enable); +} + +static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_BIAS_EN, enable); +} + +static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = (btn_high[i] * 2) / 25; + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i, + WCD939X_MBHC_BTN0_VTH, vth); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + + if (micb_num == MIC_BIAS_2) { + u8 val; + + val = FIELD_GET(WCD939X_MICB2_ENABLE, + snd_soc_component_read(component, WCD939X_ANA_MICB2)); + if (val == MICB_BIAS_ENABLE) + return true; + } + + return false; +} + +static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || + pull_up_cur < HS_PULLUP_I_3P0_UA || + pull_up_cur == HS_PULLUP_I_DEFAULT) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + dev_dbg(component->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur); +} + +static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd939x_micbias_control(component, micb_num, req, false); +} + +static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 3); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 0); + } +} + +static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + unsigned int micb_en_field, micb_vout_ctl_field; + unsigned int micb_reg, cur_vout_ctl, micb_en; + int req_vout_ctl; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_en_field = WCD939X_MICB1_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_en_field = WCD939X_MICB2_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_en_field = WCD939X_MICB3_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_en_field = WCD939X_MICB4_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd939x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + micb_en_field); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + micb_vout_ctl_field); + + req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = req_vout_ctl; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == MICB_BIAS_ENABLE) + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_PULL_DOWN); + + snd_soc_component_write_field(component, micb_reg, + micb_vout_ctl_field, req_vout_ctl); + + if (micb_en == MICB_BIAS_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + +exit: + mutex_unlock(&wcd939x->micb_lock); + return ret; +} + +static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv; + + return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +/* Selected by WCD939X_MBHC_GET_C1() */ +static const s16 wcd939x_wcd_mbhc_d1_a[4] = { + 0, 30, 30, 6 +}; + +/* Selected by zdet_param.noff */ +static const int wcd939x_mbhc_mincode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 +}; + +static const struct zdet_param wcd939x_mbhc_zdet_param = { + .ldo_ctl = 4, + .noff = 0, + .nshift = 6, + .btn5 = 0x18, + .btn6 = 0x60, + .btn7 = 0x78, +}; + +static void wcd939x_mbhc_get_result_params(struct snd_soc_component *component, + int32_t *zdet) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 x1, d1, denom; + int val; + s16 c1; + int i; + + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, true); + for (i = 0; i < WCD939X_ZDET_NUM_MEASUREMENTS; i++) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + if (val & BIT(7)) + break; + } + val = val << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, false); + x1 = WCD939X_MBHC_GET_X1(val); + c1 = WCD939X_MBHC_GET_C1(val); + + /* If ramp is not complete, give additional 5ms */ + if (c1 < 2 && x1) + mdelay(5); + + if (!c1 || !x1) { + dev_dbg(component->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + + d1 = wcd939x_wcd_mbhc_d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - zdet_param->noff)); + if (denom > 0) + *zdet = (WCD939X_ANA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < wcd939x_mbhc_mincode_param[zdet_param->noff]) + *zdet = WCD939X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB) << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + x1 = WCD939X_MBHC_GET_X1(val); + i++; + if (i == WCD939X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component, + s32 *zl, int32_t *zr) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 zdet = 0; + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_MAXV_CTL, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN5, WCD939X_MBHC_BTN5_VTH, + zdet_param->btn5); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN6, WCD939X_MBHC_BTN6_VTH, + zdet_param->btn6); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN7, WCD939X_MBHC_BTN7_VTH, + zdet_param->btn7); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_RANGE_CTL, zdet_param->noff); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_TIME_CTL, zdet_param->nshift); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL, 6); /*acc1_min_63 */ + + if (!zl) + goto z_right; + + /* Start impedance measurement for HPH_L */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, false); + + *zl = zdet; + +z_right: + if (!zr) + return; + + /* Start impedance measurement for HPH_R */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, false); + + *zr = zdet; +} + +static void wcd939x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + s32 *z_val, int flag_l_r) +{ + int q1_cal; + s16 q1; + + q1 = snd_soc_component_read(component, WCD939X_DIGITAL_EFUSE_REG_21 + flag_l_r); + if (q1 & BIT(7)) + q1_cal = (10000 - ((q1 & GENMASK(6, 0)) * 10)); + else + q1_cal = (10000 + (q1 * 10)); + + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd939x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + u32 *zl, uint32_t *zr) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev); + unsigned int reg0, reg1, reg2, reg3, reg4; + int z_mono, z_diff1, z_diff2; + bool is_fsm_disable = false; + s32 z1l, z1r, z1ls; + + reg0 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD939X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN)) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, false); + is_fsm_disable = true; + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, false); + + /* Turn off 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, + false); + + /* + * Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + wcd939x_mbhc_zdet_ramp(component, &z1l, NULL); + if (z1l == WCD939X_ZDET_FLOATING_IMPEDANCE || z1l > WCD939X_ZDET_VAL_100K) { + *zl = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zl = z1l / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd939x_mbhc_zdet_ramp(component, NULL, &z1r); + if (z1r == WCD939X_ZDET_FLOATING_IMPEDANCE || z1r > WCD939X_ZDET_VAL_100K) { + *zr = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1r / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE && + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE || + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE || + (*zl < WCD_MONO_HS_MIN_THR && *zr > WCD_MONO_HS_MIN_THR) || + (*zl > WCD_MONO_HS_MIN_THR && *zr < WCD_MONO_HS_MIN_THR)) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, true); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, true); + wcd939x_mbhc_zdet_ramp(component, &z1ls, NULL); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, false); + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, false); + + z1ls /= 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, &z1ls, 0); + + /* Parallel of left Z and 9 ohm pull down resistor */ + z_mono = (*zl * 9) / (*zl + 9); + z_diff1 = z1ls > z_mono ? z1ls - z_mono : z_mono - z1ls; + z_diff2 = *zl > z1ls ? *zl - z1ls : z1ls - *zl; + if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + z_mono))) { + dev_dbg(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + } + + /* Enable surge protection again after impedance detection */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, true); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, true); + +zdet_complete: + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN7, reg2); + + /* Turn on 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, true); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, true); + + snd_soc_component_write(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD939X_MBHC_CTL_CLK, reg3); + + if (is_fsm_disable) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, true); +} + +static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + true); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + false); + } +} + +static void wcd939x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, enable); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_L, enable); +} + +static void wcd939x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, wcd939x->mbhc_cfg.moist_rref); +} + +static void wcd939x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, + wcd939x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); +} + +static bool wcd939x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL)) + goto done; + + wcd939x_mbhc_moisture_detect_en(component, true); + + /* Read moisture comparator status, invert of status bit */ + ret = !snd_soc_component_read_field(component, WCD939X_MBHC_NEW_FSM_STATUS, + WCD939X_FSM_STATUS_HS_M_COMP_STATUS); +done: + return ret; +} + +static void wcd939x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING, + enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd939x_mbhc_clk_setup, + .mbhc_bias = wcd939x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd939x_mbhc_program_btn_thr, + .micbias_enable_status = wcd939x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd939x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd939x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd939x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd939x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd939x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd939x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd939x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd939x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd939x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd939x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd939x_mbhc_moisture_detect_en, +}; + +static int wcd939x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd939x->wcd_mbhc); + + return 0; +} + +static int wcd939x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool hphr = mc->shift; + u32 zl, zr; + + wcd_mbhc_get_impedance(wcd939x->wcd_mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + wcd939x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), +}; + +static int wcd939x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd939x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_OCP_INT); + + wcd939x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd939x->wcd_mbhc)) + return PTR_ERR(wcd939x->wcd_mbhc); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd939x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd939x->wcd_mbhc); +} + +/* END MBHC */ + +static const struct snd_kcontrol_new wcd939x_snd_controls[] = { + /* RX Path */ + SOC_SINGLE_EXT("HPHL_COMP Switch", WCD939X_COMP_L, 0, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", WCD939X_COMP_R, 1, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHL Switch", WCD939X_HPH_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD939X_HPH_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("CLSH Switch", WCD939X_CLSH, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("LO Switch", WCD939X_LO, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_L Switch", WCD939X_DSD_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_R Switch", WCD939X_DSD_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("HPHL Volume", WCD939X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD939X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, + wcd939x_ldoh_get, wcd939x_ldoh_put), + + /* TX Path */ + SOC_SINGLE_EXT("ADC1 Switch", WCD939X_ADC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD939X_ADC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD939X_ADC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC4 Switch", WCD939X_ADC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD939X_DMIC0, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD939X_DMIC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD939X_MBHC, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD939X_DMIC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD939X_DMIC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD939X_DMIC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD939X_DMIC5, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC6 Switch", WCD939X_DMIC6, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC7 Switch", WCD939X_DMIC7, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("ADC1 Volume", WCD939X_ANA_TX_CH1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD939X_ANA_TX_CH2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD939X_ANA_TX_CH3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD939X_ANA_TX_CH4, 0, 20, 0, + analog_gain), +}; + +static const struct snd_soc_dapm_widget wcd939x_dapm_widgets[] = { + /*input widgets*/ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_MIC("Analog Mic1", NULL), + SND_SOC_DAPM_MIC("Analog Mic2", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux), + SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux), + + /* tx mixers */ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, + adc3_switch, ARRAY_SIZE(adc3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0, + adc4_switch, ARRAY_SIZE(adc4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, + dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0, + dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0, + dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0, + dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0, + dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0, + dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0, + dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0, + dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* micbias widgets */ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* micbias pull up widgets */ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* output widgets tx */ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"), + + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_EAR"), + + /* rx widgets */ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD939X_ANA_EAR, 7, 0, NULL, 0, + wcd939x_codec_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_PGA_E("HPHL PGA", WCD939X_ANA_HPH, 7, 0, NULL, 0, + wcd939x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD939X_ANA_HPH, 6, 0, NULL, 0, + wcd939x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_rxclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + + /* rx mixer widgets */ + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /* output widgets rx */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), +}; + +static const struct snd_soc_dapm_route wcd939x_audio_map[] = { + /* TX Path */ + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1 REQ"}, + {"ADC1 REQ", NULL, "ADC1"}, + {"ADC1", NULL, "ADC1 MUX"}, + {"ADC1 MUX", "CH1_AMIC1", "AMIC1"}, + {"ADC1 MUX", "CH1_AMIC2", "AMIC2"}, + {"ADC1 MUX", "CH1_AMIC3", "AMIC3"}, + {"ADC1 MUX", "CH1_AMIC4", "AMIC4"}, + {"ADC1 MUX", "CH1_AMIC5", "AMIC5"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2 REQ"}, + {"ADC2 REQ", NULL, "ADC2"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "CH2_AMIC1", "AMIC1"}, + {"ADC2 MUX", "CH2_AMIC2", "AMIC2"}, + {"ADC2 MUX", "CH2_AMIC3", "AMIC3"}, + {"ADC2 MUX", "CH2_AMIC4", "AMIC4"}, + {"ADC2 MUX", "CH2_AMIC5", "AMIC5"}, + + {"ADC3_OUTPUT", NULL, "ADC3_MIXER"}, + {"ADC3_MIXER", "Switch", "ADC3 REQ"}, + {"ADC3 REQ", NULL, "ADC3"}, + {"ADC3", NULL, "ADC3 MUX"}, + {"ADC3 MUX", "CH3_AMIC1", "AMIC1"}, + {"ADC3 MUX", "CH3_AMIC3", "AMIC3"}, + {"ADC3 MUX", "CH3_AMIC4", "AMIC4"}, + {"ADC3 MUX", "CH3_AMIC5", "AMIC5"}, + + {"ADC4_OUTPUT", NULL, "ADC4_MIXER"}, + {"ADC4_MIXER", "Switch", "ADC4 REQ"}, + {"ADC4 REQ", NULL, "ADC4"}, + {"ADC4", NULL, "ADC4 MUX"}, + {"ADC4 MUX", "CH4_AMIC1", "AMIC1"}, + {"ADC4 MUX", "CH4_AMIC3", "AMIC3"}, + {"ADC4 MUX", "CH4_AMIC4", "AMIC4"}, + {"ADC4 MUX", "CH4_AMIC5", "AMIC5"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, + + {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, + {"DMIC3_MIXER", "Switch", "DMIC3"}, + + {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, + {"DMIC4_MIXER", "Switch", "DMIC4"}, + + {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, + {"DMIC5_MIXER", "Switch", "DMIC5"}, + + {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, + {"DMIC6_MIXER", "Switch", "DMIC6"}, + + {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"}, + {"DMIC7_MIXER", "Switch", "DMIC7"}, + + {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"}, + {"DMIC8_MIXER", "Switch", "DMIC8"}, + + /* RX Path */ + {"IN1_HPHL", NULL, "VDD_BUCK"}, + {"IN1_HPHL", NULL, "CLS_H_PORT"}, + + {"RX1", NULL, "IN1_HPHL"}, + {"RX1", NULL, "RXCLK"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "VDD_BUCK"}, + {"IN2_HPHR", NULL, "CLS_H_PORT"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"RX2", NULL, "RXCLK"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"IN3_EAR", NULL, "VDD_BUCK"}, + {"RX3", NULL, "IN3_EAR"}, + {"RX3", NULL, "RXCLK"}, + + {"RDAC3_MUX", "RX3", "RX3"}, + {"RDAC3_MUX", "RX1", "RX1"}, + {"RDAC3", NULL, "RDAC3_MUX"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, +}; + +static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + + /* set micbias voltage */ + vout_ctl_1 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb1_mv); + vout_ctl_2 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb2_mv); + vout_ctl_3 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb3_mv); + vout_ctl_4 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0) + return -EINVAL; + + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1, + WCD939X_MICB1_VOUT_CTL, vout_ctl_1); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2, + WCD939X_MICB2_VOUT_CTL, vout_ctl_2); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3, + WCD939X_MICB3_VOUT_CTL, vout_ctl_3); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4, + WCD939X_MICB4_VOUT_CTL, vout_ctl_4); + + return 0; +} + +static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) +{ + /* + * HPHR/HPHL/EAR Watchdog interrupt threaded handler + * + * Watchdog interrupts are expected to be enabled when switching + * on the HPHL/R and EAR RX PGA in order to make sure the interrupts + * are acked by the regmap_irq handler to allow PDM sync. + * We could leave those interrupts masked but we would not have + * any valid way to enable/disable them without violating irq layers. + * + * The HPHR/HPHL/EAR Watchdog interrupts are handled + * by regmap_irq, so requesting a threaded handler is the + * safest way to be able to ack those interrupts without + * colliding with the regmap_irq setup. + */ + + return IRQ_HANDLED; +} + +/* + * Setup a virtual interrupt domain to hook regmap_irq + * The root domain will have a single interrupt which mapping + * will trigger the regmap_irq handler. + * + * root: + * wcd_irq_chip + * [0] wcd939x_regmap_irq_chip + * [0] MBHC_BUTTON_PRESS_DET + * [1] MBHC_BUTTON_RELEASE_DET + * ... + * [16] HPHR_SURGE_DET_INT + * + * Interrupt trigger: + * soundwire_interrupt_callback() + * \-handle_nested_irq(0) + * \- regmap_irq_thread() + * \- handle_nested_irq(i) + */ +static struct irq_chip wcd_irq_chip = { + .name = "WCD939x", +}; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev) +{ + wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(wcd->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + return devm_regmap_add_irq_chip(dev, wcd->regmap, + irq_create_mapping(wcd->virq, 0), + IRQF_ONESHOT, 0, &wcd939x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd939x_soc_codec_probe(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int ret, i; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(2000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd939x->regmap); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + wcd939x->variant = snd_soc_component_read_field(component, + WCD939X_DIGITAL_EFUSE_REG_0, + WCD939X_EFUSE_REG_0_WCD939X_ID); + + wcd939x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD939X); + if (IS_ERR(wcd939x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd939x->clsh_info); + } + + wcd939x_io_init(component); + + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd939x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd939x->regmap, + (WCD939X_DIGITAL_INTR_LEVEL_0 + i), 0); + + pm_runtime_put(dev); + + /* Request for watchdog interrupt */ + wcd939x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_PDM_WD_INT); + wcd939x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_PDM_WD_INT); + wcd939x->ear_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_EAR_PDM_WD_INT); + + ret = request_threaded_irq(wcd939x->hphr_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret); + goto err_free_clsh_ctrl; + } + + ret = request_threaded_irq(wcd939x->hphl_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret); + goto err_free_hphr_pdm_wd_int; + } + + ret = request_threaded_irq(wcd939x->ear_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret); + goto err_free_hphl_pdm_wd_int; + } + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + + switch (wcd939x->variant) { + case WCD9390: + ret = snd_soc_add_component_controls(component, wcd9390_snd_controls, + ARRAY_SIZE(wcd9390_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + case WCD9395: + ret = snd_soc_add_component_controls(component, wcd9395_snd_controls, + ARRAY_SIZE(wcd9395_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + default: + break; + } + + ret = wcd939x_mbhc_init(component); + if (ret) { + dev_err(component->dev, "mbhc initialization failed\n"); + goto err_free_ear_pdm_wd_int; + } + + return 0; + +err_free_ear_pdm_wd_int: + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); +err_free_hphl_pdm_wd_int: + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); +err_free_hphr_pdm_wd_int: + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); +err_free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd939x->clsh_info); + + return ret; +} + +static void wcd939x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd939x_mbhc_deinit(component); + + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); + + wcd_clsh_ctrl_free(wcd939x->clsh_info); +} + +static int wcd939x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd939x_priv *wcd = dev_get_drvdata(comp->dev); + + if (jack) + return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + + wcd_mbhc_stop(wcd->wcd_mbhc); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd939x = { + .name = "wcd939x_codec", + .probe = wcd939x_soc_codec_probe, + .remove = wcd939x_soc_codec_remove, + .controls = wcd939x_snd_controls, + .num_controls = ARRAY_SIZE(wcd939x_snd_controls), + .dapm_widgets = wcd939x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd939x_dapm_widgets), + .dapm_routes = wcd939x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd939x_audio_map), + .set_jack = wcd939x_codec_set_jack, + .endianness = 1, +}; + +#if IS_ENABLED(CONFIG_TYPEC) +/* Get USB-C plug orientation to provide swap event for MBHC */ +static int wcd939x_typec_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct wcd939x_priv *wcd939x = typec_switch_get_drvdata(sw); + + wcd939x->typec_orientation = orientation; + + return 0; +} + +static int wcd939x_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct wcd939x_priv *wcd939x = typec_mux_get_drvdata(mux); + unsigned int previous_mode = wcd939x->typec_mode; + + if (!wcd939x->wcd_mbhc) + return -EINVAL; + + if (wcd939x->typec_mode != state->mode) { + wcd939x->typec_mode = state->mode; + + if (wcd939x->typec_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_plug(wcd939x->wcd_mbhc); + else if (previous_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_unplug(wcd939x->wcd_mbhc); + } + + return 0; +} +#endif /* CONFIG_TYPEC */ + +static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int rc = 0; + + rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!rc) + wcd->micb1_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias1 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!rc) + wcd->micb2_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias2 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!rc) + wcd->micb3_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias3 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val); + if (!rc) + wcd->micb4_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); +} + +#if IS_ENABLED(CONFIG_TYPEC) +static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (!wcd939x->typec_analog_mux || !wcd939x->typec_switch) + return false; + + /* Report inversion via Type Switch of USBSS */ + typec_switch_set(wcd939x->typec_switch, + wcd939x->typec_orientation == TYPEC_ORIENTATION_REVERSE ? + TYPEC_ORIENTATION_NORMAL : TYPEC_ORIENTATION_REVERSE); + + return true; +} +#endif /* CONFIG_TYPEC */ + +static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device *dev) +{ + struct wcd_mbhc_config *cfg = &wcd939x->mbhc_cfg; +#if IS_ENABLED(CONFIG_TYPEC) + struct device_node *np; +#endif /* CONFIG_TYPEC */ + int ret; + + wcd939x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + if (wcd939x->reset_gpio < 0) + return dev_err_probe(dev, wcd939x->reset_gpio, + "Failed to get reset gpio\n"); + + wcd939x->supplies[0].supply = "vdd-rxtx"; + wcd939x->supplies[1].supply = "vdd-io"; + wcd939x->supplies[2].supply = "vdd-buck"; + wcd939x->supplies[3].supply = "vdd-mic-bias"; + + ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + if (ret) { + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + } + + wcd939x_dt_parse_micbias_info(dev, wcd939x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd939x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is node has a port and a valid remote endpoint + * consider HP lines are connected to the USBSS part + */ + np = of_graph_get_remote_node(dev->of_node, 0, 0); + if (np) { + wcd939x->typec_analog_mux = true; + cfg->typec_analog_mux = true; + cfg->swap_gnd_mic = wcd939x_swap_gnd_mic; + } +#endif /* CONFIG_TYPEC */ + + return 0; +} + +static int wcd939x_reset(struct wcd939x_priv *wcd939x) +{ + gpio_direction_output(wcd939x->reset_gpio, 0); + /* 20us sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + gpio_set_value(wcd939x->reset_gpio, 1); + /* 20us sleep required after pulling the reset gpio to HIGH */ + usleep_range(20, 30); + + return 0; +} + +static int wcd939x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd939x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_free(wcd, substream, dai); +} + +static int wcd939x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_set_sdw_stream(wcd, dai, stream, direction); +} + +static const struct snd_soc_dai_ops wcd939x_sdw_dai_ops = { + .hw_params = wcd939x_codec_hw_params, + .hw_free = wcd939x_codec_free, + .set_stream = wcd939x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd939x_dais[] = { + [0] = { + .name = "wcd939x-sdw-rx", + .playback = { + .stream_name = "WCD AIF1 Playback", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd939x_sdw_dai_ops, + }, + [1] = { + .name = "wcd939x-sdw-tx", + .capture = { + .stream_name = "WCD AIF1 Capture", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd939x_sdw_dai_ops, + }, +}; + +static int wcd939x_bind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + unsigned int version, id1, status1; + int ret; + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Get USBSS type-c switch to send gnd/mic swap events + * typec_switch is fetched now to avoid a probe deadlock since + * the USBSS depends on the typec_mux register in wcd939x_probe() + */ + if (wcd939x->typec_analog_mux) { + wcd939x->typec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(wcd939x->typec_switch)) + return dev_err_probe(dev, PTR_ERR(wcd939x->typec_switch), + "failed to acquire orientation-switch\n"); + } +#endif /* CONFIG_TYPEC */ + + ret = component_bind_all(dev, wcd939x); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + goto err_put_typec_switch; + } + + wcd939x->rxdev = wcd939x_sdw_device_get(wcd939x->rxnode); + if (!wcd939x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + ret = -EINVAL; + goto err_unbind; + } + wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev); + wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x; + + wcd939x->txdev = wcd939x_sdw_device_get(wcd939x->txnode); + if (!wcd939x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + ret = -EINVAL; + goto err_put_rxdev; + } + wcd939x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd939x->txdev); + wcd939x->sdw_priv[AIF1_CAP]->wcd939x = wcd939x; + wcd939x->tx_sdw_dev = dev_to_sdw_dev(wcd939x->txdev); + + /* + * As TX is main CSR reg interface, which should not be suspended first. + * explicitly add the dependency link + */ + if (!device_link_add(wcd939x->rxdev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink tx and rx\n"); + ret = -EINVAL; + goto err_put_txdev; + } + + if (!device_link_add(dev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and tx\n"); + ret = -EINVAL; + goto err_remove_rxtx_link; + } + + if (!device_link_add(dev, wcd939x->rxdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and rx\n"); + ret = -EINVAL; + goto err_remove_tx_link; + } + + /* Get regmap from TX SoundWire device */ + wcd939x->regmap = wcd939x_swr_get_regmap(wcd939x->sdw_priv[AIF1_CAP]); + if (IS_ERR(wcd939x->regmap)) { + dev_err(dev, "could not get TX device regmap\n"); + ret = PTR_ERR(wcd939x->regmap); + goto err_remove_rx_link; + } + + ret = wcd939x_irq_init(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret); + goto err_remove_rx_link; + } + + wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq; + wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq; + + ret = wcd939x_set_micbias_data(wcd939x); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_remove_rx_link; + } + + /* Check WCD9395 version */ + regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1); + regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1); + + if (id1 == 0) + version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); + else + version = WCD939X_VERSION_2_0; + + dev_dbg(dev, "wcd939x version: %s\n", version_to_str(version)); + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd939x, + wcd939x_dais, ARRAY_SIZE(wcd939x_dais)); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_remove_rx_link; + } + + return 0; + +err_remove_rx_link: + device_link_remove(dev, wcd939x->rxdev); +err_remove_tx_link: + device_link_remove(dev, wcd939x->txdev); +err_remove_rxtx_link: + device_link_remove(wcd939x->rxdev, wcd939x->txdev); +err_put_txdev: + put_device(wcd939x->txdev); +err_put_rxdev: + put_device(wcd939x->rxdev); +err_unbind: + component_unbind_all(dev, wcd939x); +err_put_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_put(wcd939x->typec_switch); +#endif /* CONFIG_TYPEC */ + + return ret; +} + +static void wcd939x_unbind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd939x->txdev); + device_link_remove(dev, wcd939x->rxdev); + device_link_remove(wcd939x->rxdev, wcd939x->txdev); + put_device(wcd939x->txdev); + put_device(wcd939x->rxdev); + component_unbind_all(dev, wcd939x); +} + +static const struct component_master_ops wcd939x_comp_ops = { + .bind = wcd939x_bind, + .unbind = wcd939x_unbind, +}; + +static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np = dev->of_node; + + wcd939x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd939x->rxnode) { + dev_err(dev, "%s: Rx-device node not defined\n", __func__); + return -ENODEV; + } + + of_node_get(wcd939x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->rxnode); + + wcd939x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd939x->txnode) { + dev_err(dev, "%s: Tx-device node not defined\n", __func__); + return -ENODEV; + } + of_node_get(wcd939x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->txnode); + return 0; +} + +static int wcd939x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct wcd939x_priv *wcd939x = NULL; + struct device *dev = &pdev->dev; + int ret; + + wcd939x = devm_kzalloc(dev, sizeof(struct wcd939x_priv), + GFP_KERNEL); + if (!wcd939x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd939x); + mutex_init(&wcd939x->micb_lock); + + ret = wcd939x_populate_dt_data(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + return -EINVAL; + } + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is USBSS is used to mux analog lines, + * register a typec mux/switch to get typec events + */ + if (wcd939x->typec_analog_mux) { + struct typec_mux_desc mux_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_mux_set, + }; + struct typec_switch_desc sw_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_switch_set, + }; + + wcd939x->typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(wcd939x->typec_mux)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_mux), + "failed to register typec mux\n"); + goto err_disable_regulators; + } + + wcd939x->typec_sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(wcd939x->typec_sw)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_sw), + "failed to register typec switch\n"); + goto err_unregister_typec_mux; + } + } +#endif /* CONFIG_TYPEC */ + + ret = wcd939x_add_slave_components(wcd939x, dev, &match); + if (ret) + goto err_unregister_typec_switch; + + wcd939x_reset(wcd939x); + + ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match); + if (ret) + goto err_disable_regulators; + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +#if IS_ENABLED(CONFIG_TYPEC) +err_unregister_typec_mux: + if (wcd939x->typec_analog_mux) + typec_mux_unregister(wcd939x->typec_mux); +#endif /* CONFIG_TYPEC */ + +err_unregister_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_unregister(wcd939x->typec_sw); +#endif /* CONFIG_TYPEC */ + +err_disable_regulators: + regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); + + return ret; +} + +static void wcd939x_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + + component_master_del(dev, &wcd939x_comp_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); + regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); +} + +#if defined(CONFIG_OF) +static const struct of_device_id wcd939x_dt_match[] = { + { .compatible = "qcom,wcd9390-codec" }, + { .compatible = "qcom,wcd9395-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, wcd939x_dt_match); +#endif + +static struct platform_driver wcd939x_codec_driver = { + .probe = wcd939x_probe, + .remove_new = wcd939x_remove, + .driver = { + .name = "wcd939x_codec", + .of_match_table = of_match_ptr(wcd939x_dt_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd939x_codec_driver); +MODULE_DESCRIPTION("WCD939X Codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h new file mode 100644 index 000000000000..807cf3113d20 --- /dev/null +++ b/sound/soc/codecs/wcd939x.h @@ -0,0 +1,989 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __WCD939X_H__ +#define __WCD939X_H__ +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> + +#define WCD939X_BASE (0x3000) +#define WCD939X_ANA_PAGE (0x3000) +#define WCD939X_ANA_BIAS (0x3001) +#define WCD939X_BIAS_ANALOG_BIAS_EN BIT(7) +#define WCD939X_BIAS_PRECHRG_EN BIT(6) +#define WCD939X_BIAS_PRECHRG_CTL_MODE BIT(5) +#define WCD939X_ANA_RX_SUPPLIES (0x3008) +#define WCD939X_RX_SUPPLIES_VPOS_EN BIT(7) +#define WCD939X_RX_SUPPLIES_VNEG_EN BIT(6) +#define WCD939X_RX_SUPPLIES_VPOS_PWR_LVL BIT(3) +#define WCD939X_RX_SUPPLIES_VNEG_PWR_LVL BIT(2) +#define WCD939X_RX_SUPPLIES_REGULATOR_MODE BIT(1) +#define WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE BIT(0) +#define WCD939X_ANA_HPH (0x3009) +#define WCD939X_HPH_HPHL_ENABLE BIT(7) +#define WCD939X_HPH_HPHR_ENABLE BIT(6) +#define WCD939X_HPH_HPHL_REF_ENABLE BIT(5) +#define WCD939X_HPH_HPHR_REF_ENABLE BIT(4) +#define WCD939X_HPH_PWR_LEVEL GENMASK(3, 2) +#define WCD939X_ANA_EAR (0x300a) +#define WCD939X_ANA_EAR_COMPANDER_CTL (0x300b) +#define WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG BIT(7) +#define WCD939X_EAR_COMPANDER_CTL_EAR_GAIN GENMASK(6, 2) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_BYP BIT(1) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_CLK_EDGE BIT(0) +#define WCD939X_ANA_TX_CH1 (0x300e) +#define WCD939X_ANA_TX_CH2 (0x300f) +#define WCD939X_TX_CH2_ENABLE BIT(7) +#define WCD939X_TX_CH2_HPF1_INIT BIT(6) +#define WCD939X_TX_CH2_HPF2_INIT BIT(5) +#define WCD939X_TX_CH2_GAIN GENMASK(4, 0) +#define WCD939X_ANA_TX_CH3 (0x3010) +#define WCD939X_ANA_TX_CH4 (0x3011) +#define WCD939X_TX_CH4_ENABLE BIT(7) +#define WCD939X_TX_CH4_HPF3_INIT BIT(6) +#define WCD939X_TX_CH4_HPF4_INIT BIT(5) +#define WCD939X_TX_CH4_GAIN GENMASK(4, 0) +#define WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012) +#define WCD939X_ANA_MICB3_DSP_EN_LOGIC (0x3013) +#define WCD939X_ANA_MBHC_MECH (0x3014) +#define WCD939X_MBHC_MECH_L_DET_EN BIT(7) +#define WCD939X_MBHC_MECH_GND_DET_EN BIT(6) +#define WCD939X_MBHC_MECH_MECH_DETECT_TYPE BIT(5) +#define WCD939X_MBHC_MECH_HPHL_PLUG_TYPE BIT(4) +#define WCD939X_MBHC_MECH_GND_PLUG_TYPE BIT(3) +#define WCD939X_MBHC_MECH_MECH_HS_L_PULLUP_COMP_EN BIT(2) +#define WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN BIT(1) +#define WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND BIT(0) +#define WCD939X_ANA_MBHC_ELECT (0x3015) +#define WCD939X_MBHC_ELECT_FSM_EN BIT(7) +#define WCD939X_MBHC_ELECT_BTNDET_ISRC_CTL GENMASK(6, 4) +#define WCD939X_MBHC_ELECT_ELECT_DET_TYPE BIT(3) +#define WCD939X_MBHC_ELECT_ELECT_SCHMT_ISRC_CTL GENMASK(2, 1) +#define WCD939X_MBHC_ELECT_BIAS_EN BIT(0) +#define WCD939X_ANA_MBHC_ZDET (0x3016) +#define WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN BIT(7) +#define WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN BIT(6) +#define WCD939X_MBHC_ZDET_ZDET_CHG_EN BIT(5) +#define WCD939X_MBHC_ZDET_ZDET_ILEAK_COMP_EN BIT(4) +#define WCD939X_MBHC_ZDET_ELECT_ISRC_EN BIT(1) +#define WCD939X_ANA_MBHC_RESULT_1 (0x3017) +#define WCD939X_MBHC_RESULT_1_Z_RESULT_LSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_2 (0x3018) +#define WCD939X_MBHC_RESULT_2_Z_RESULT_MSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_3 (0x3019) +#define WCD939X_ANA_MBHC_BTN0 (0x301a) +#define WCD939X_MBHC_BTN0_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN1 (0x301b) +#define WCD939X_MBHC_BTN1_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN2 (0x301c) +#define WCD939X_MBHC_BTN2_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN3 (0x301d) +#define WCD939X_MBHC_BTN3_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN4 (0x301e) +#define WCD939X_MBHC_BTN4_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN5 (0x301f) +#define WCD939X_MBHC_BTN5_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN6 (0x3020) +#define WCD939X_MBHC_BTN6_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN7 (0x3021) +#define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2) +#define WCD939X_ANA_MICB1 (0x3022) +#define WCD939X_MICB1_ENABLE GENMASK(7, 6) +#define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2 (0x3023) +#define WCD939X_MICB2_ENABLE GENMASK(7, 6) +#define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2_RAMP (0x3024) +#define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7) +#define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE BIT(6) +#define WCD939X_MICB2_RAMP_ALLSW_OVRD_ENABLE BIT(5) +#define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2) +#define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0) +#define WCD939X_ANA_MICB3 (0x3025) +#define WCD939X_MICB3_ENABLE GENMASK(7, 6) +#define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB4 (0x3026) +#define WCD939X_MICB4_ENABLE GENMASK(7, 6) +#define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0) +#define WCD939X_BIAS_CTL (0x3028) +#define WCD939X_BIAS_VBG_FINE_ADJ (0x3029) +#define WCD939X_LDOL_VDDCX_ADJUST (0x3040) +#define WCD939X_LDOL_DISABLE_LDOL (0x3041) +#define WCD939X_MBHC_CTL_CLK (0x3056) +#define WCD939X_MBHC_CTL_ANA (0x3057) +#define WCD939X_MBHC_ZDET_VNEG_CTL (0x3058) +#define WCD939X_MBHC_ZDET_BIAS_CTL (0x3059) +#define WCD939X_MBHC_CTL_BCS (0x305a) +#define WCD939X_MBHC_MOISTURE_DET_FSM_STATUS (0x305b) +#define WCD939X_MBHC_TEST_CTL (0x305c) +#define WCD939X_LDOH_MODE (0x3067) +#define WCD939X_MODE_LDOH_EN BIT(7) +#define WCD939X_MODE_PWRDN_STATE BIT(6) +#define WCD939X_MODE_SLOWRAMP_EN BIT(5) +#define WCD939X_MODE_VOUT_ADJUST GENMASK(4, 3) +#define WCD939X_MODE_VOUT_COARSE_ADJ GENMASK(2, 0) +#define WCD939X_LDOH_BIAS (0x3068) +#define WCD939X_LDOH_STB_LOADS (0x3069) +#define WCD939X_LDOH_SLOWRAMP (0x306a) +#define WCD939X_MICB1_TEST_CTL_1 (0x306b) +#define WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL GENMASK(7, 5) +#define WCD939X_TEST_CTL_1_EN_VREFGEN BIT(4) +#define WCD939X_TEST_CTL_1_EN_LDO BIT(3) +#define WCD939X_TEST_CTL_1_LDO_BLEEDER_I_CTRL GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_2 (0x306c) +#define WCD939X_TEST_CTL_2_IBIAS_VREFGEN GENMASK(7, 6) +#define WCD939X_TEST_CTL_2_INRUSH_CURRENT_FIX_DIS BIT(5) +#define WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_3 (0x306d) +#define WCD939X_TEST_CTL_3_CFILT_REF_EN BIT(7) +#define WCD939X_TEST_CTL_3_RZ_LDO_VAL GENMASK(6, 4) +#define WCD939X_TEST_CTL_3_IBIAS_LDO_STG3 GENMASK(3, 2) +#define WCD939X_TEST_CTL_3_ATEST_CTRL GENMASK(1, 0) +#define WCD939X_MICB2_TEST_CTL_1 (0x306e) +#define WCD939X_MICB2_TEST_CTL_2 (0x306f) +#define WCD939X_MICB2_TEST_CTL_3 (0x3070) +#define WCD939X_MICB3_TEST_CTL_1 (0x3071) +#define WCD939X_MICB3_TEST_CTL_2 (0x3072) +#define WCD939X_MICB3_TEST_CTL_3 (0x3073) +#define WCD939X_MICB4_TEST_CTL_1 (0x3074) +#define WCD939X_MICB4_TEST_CTL_2 (0x3075) +#define WCD939X_MICB4_TEST_CTL_3 (0x3076) +#define WCD939X_TX_COM_ADC_VCM (0x3077) +#define WCD939X_TX_COM_BIAS_ATEST (0x3078) +#define WCD939X_TX_COM_SPARE1 (0x3079) +#define WCD939X_TX_COM_SPARE2 (0x307a) +#define WCD939X_TX_COM_TXFE_DIV_CTL (0x307b) +#define WCD939X_TX_COM_TXFE_DIV_START (0x307c) +#define WCD939X_TX_COM_SPARE3 (0x307d) +#define WCD939X_TX_COM_SPARE4 (0x307e) +#define WCD939X_TX_1_2_TEST_EN (0x307f) +#define WCD939X_TX_1_2_ADC_IB (0x3080) +#define WCD939X_TX_1_2_ATEST_REFCTL (0x3081) +#define WCD939X_TX_1_2_TEST_CTL (0x3082) +#define WCD939X_TX_1_2_TEST_BLK_EN1 (0x3083) +#define WCD939X_TX_1_2_TXFE1_CLKDIV (0x3084) +#define WCD939X_TX_1_2_SAR2_ERR (0x3085) +#define WCD939X_TX_1_2_SAR1_ERR (0x3086) +#define WCD939X_TX_3_4_TEST_EN (0x3087) +#define WCD939X_TX_3_4_ADC_IB (0x3088) +#define WCD939X_TX_3_4_ATEST_REFCTL (0x3089) +#define WCD939X_TX_3_4_TEST_CTL (0x308a) +#define WCD939X_TX_3_4_TEST_BLK_EN3 (0x308b) +#define WCD939X_TX_3_4_TXFE3_CLKDIV (0x308c) +#define WCD939X_TX_3_4_SAR4_ERR (0x308d) +#define WCD939X_TX_3_4_SAR3_ERR (0x308e) +#define WCD939X_TX_3_4_TEST_BLK_EN2 (0x308f) +#define WCD939X_TEST_BLK_EN2_ADC2_INT1_EN BIT(7) +#define WCD939X_TEST_BLK_EN2_ADC2_INT2_EN BIT(6) +#define WCD939X_TEST_BLK_EN2_ADC2_SAR_EN BIT(5) +#define WCD939X_TEST_BLK_EN2_ADC2_CMGEN_EN BIT(4) +#define WCD939X_TEST_BLK_EN2_ADC2_CLKGEN_EN BIT(3) +#define WCD939X_TEST_BLK_EN2_ADC12_VREF_NONL2 GENMASK(2, 1) +#define WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN BIT(0) +#define WCD939X_TX_3_4_TXFE2_CLKDIV (0x3090) +#define WCD939X_TX_3_4_SPARE1 (0x3091) +#define WCD939X_TX_3_4_TEST_BLK_EN4 (0x3092) +#define WCD939X_TX_3_4_TXFE4_CLKDIV (0x3093) +#define WCD939X_TX_3_4_SPARE2 (0x3094) +#define WCD939X_CLASSH_MODE_1 (0x3097) +#define WCD939X_CLASSH_MODE_2 (0x3098) +#define WCD939X_CLASSH_MODE_3 (0x3099) +#define WCD939X_CLASSH_CTRL_VCL_1 (0x309a) +#define WCD939X_CLASSH_CTRL_VCL_2 (0x309b) +#define WCD939X_CLASSH_CTRL_CCL_1 (0x309c) +#define WCD939X_CLASSH_CTRL_CCL_2 (0x309d) +#define WCD939X_CLASSH_CTRL_CCL_3 (0x309e) +#define WCD939X_CLASSH_CTRL_CCL_4 (0x309f) +#define WCD939X_CLASSH_CTRL_CCL_5 (0x30a0) +#define WCD939X_CLASSH_BUCK_TMUX_A_D (0x30a1) +#define WCD939X_CLASSH_BUCK_SW_DRV_CNTL (0x30a2) +#define WCD939X_CLASSH_SPARE (0x30a3) +#define WCD939X_FLYBACK_EN (0x30a4) +#define WCD939X_FLYBACK_VNEG_CTRL_1 (0x30a5) +#define WCD939X_FLYBACK_VNEG_CTRL_2 (0x30a6) +#define WCD939X_FLYBACK_VNEG_CTRL_3 (0x30a7) +#define WCD939X_FLYBACK_VNEG_CTRL_4 (0x30a8) +#define WCD939X_VNEG_CTRL_4_ILIM_SEL GENMASK(7, 4) +#define WCD939X_VNEG_CTRL_4_PW_BUF_POS GENMASK(3, 2) +#define WCD939X_VNEG_CTRL_4_PW_BUF_NEG GENMASK(1, 0) +#define WCD939X_FLYBACK_VNEG_CTRL_5 (0x30a9) +#define WCD939X_FLYBACK_VNEG_CTRL_6 (0x30aa) +#define WCD939X_FLYBACK_VNEG_CTRL_7 (0x30ab) +#define WCD939X_FLYBACK_VNEG_CTRL_8 (0x30ac) +#define WCD939X_FLYBACK_VNEG_CTRL_9 (0x30ad) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_1 (0x30ae) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_2 (0x30af) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_3 (0x30b0) +#define WCD939X_FLYBACK_CTRL_1 (0x30b1) +#define WCD939X_FLYBACK_TEST_CTL (0x30b2) +#define WCD939X_RX_AUX_SW_CTL (0x30b3) +#define WCD939X_RX_PA_AUX_IN_CONN (0x30b4) +#define WCD939X_RX_TIMER_DIV (0x30b5) +#define WCD939X_RX_OCP_CTL (0x30b6) +#define WCD939X_RX_OCP_COUNT (0x30b7) +#define WCD939X_RX_BIAS_EAR_DAC (0x30b8) +#define WCD939X_RX_BIAS_EAR_AMP (0x30b9) +#define WCD939X_RX_BIAS_HPH_LDO (0x30ba) +#define WCD939X_RX_BIAS_HPH_PA (0x30bb) +#define WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30bc) +#define WCD939X_RX_BIAS_HPH_RDAC_LDO (0x30bd) +#define WCD939X_RX_BIAS_HPH_CNP1 (0x30be) +#define WCD939X_RX_BIAS_HPH_LOWPOWER (0x30bf) +#define WCD939X_RX_BIAS_AUX_DAC (0x30c0) +#define WCD939X_RX_BIAS_AUX_AMP (0x30c1) +#define WCD939X_RX_BIAS_VNEGDAC_BLEEDER (0x30c2) +#define WCD939X_RX_BIAS_MISC (0x30c3) +#define WCD939X_RX_BIAS_BUCK_RST (0x30c4) +#define WCD939X_RX_BIAS_BUCK_VREF_ERRAMP (0x30c5) +#define WCD939X_RX_BIAS_FLYB_ERRAMP (0x30c6) +#define WCD939X_RX_BIAS_FLYB_BUFF (0x30c7) +#define WCD939X_RX_BIAS_FLYB_MID_RST (0x30c8) +#define WCD939X_HPH_L_STATUS (0x30c9) +#define WCD939X_HPH_R_STATUS (0x30ca) +#define WCD939X_HPH_CNP_EN (0x30cb) +#define WCD939X_HPH_CNP_WG_CTL (0x30cc) +#define WCD939X_HPH_CNP_WG_TIME (0x30cd) +#define WCD939X_HPH_OCP_CTL (0x30ce) +#define WCD939X_OCP_CTL_OCP_CURR_LIMIT GENMASK(7, 5) +#define WCD939X_OCP_CTL_OCP_FSM_EN BIT(4) +#define WCD939X_OCP_CTL_SPARE_BITS BIT(3) +#define WCD939X_OCP_CTL_SCD_OP_EN BIT(1) +#define WCD939X_HPH_AUTO_CHOP (0x30cf) +#define WCD939X_HPH_CHOP_CTL (0x30d0) +#define WCD939X_HPH_PA_CTL1 (0x30d1) +#define WCD939X_HPH_PA_CTL2 (0x30d2) +#define WCD939X_PA_CTL2_HPHPA_GND_R BIT(6) +#define WCD939X_PA_CTL2_HPHPA_GND_L BIT(4) +#define WCD939X_PA_CTL2_GM3_CASCODE_CTL_NORMAL GENMASK(1, 0) +#define WCD939X_HPH_L_EN (0x30d3) +#define WCD939X_L_EN_CONST_SEL_L GENMASK(7, 6) +#define WCD939X_L_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_L_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_L_TEST (0x30d4) +#define WCD939X_HPH_L_ATEST (0x30d5) +#define WCD939X_HPH_R_EN (0x30d6) +#define WCD939X_R_EN_CONST_SEL_R GENMASK(7, 6) +#define WCD939X_R_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_R_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_R_TEST (0x30d7) +#define WCD939X_HPH_R_ATEST (0x30d8) +#define WCD939X_R_ATEST_DACR_REF_ATEST1_CONN BIT(7) +#define WCD939X_R_ATEST_LDO1_R_ATEST2_CONN BIT(6) +#define WCD939X_R_ATEST_LDO_R_ATEST2_CAL BIT(5) +#define WCD939X_R_ATEST_LDO2_R_ATEST2_CONN BIT(4) +#define WCD939X_R_ATEST_LDO_1P65V_ATEST1_CONN BIT(3) +#define WCD939X_R_ATEST_HPH_GND_OVR BIT(1) +#define WCD939X_HPH_RDAC_CLK_CTL1 (0x30d9) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN BIT(7) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_DIV_CTRL GENMASK(6, 4) +#define WCD939X_RDAC_CLK_CTL1_SPARE_BITS GENMASK(3, 0) +#define WCD939X_HPH_RDAC_CLK_CTL2 (0x30da) +#define WCD939X_HPH_RDAC_LDO_CTL (0x30db) +#define WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30dc) +#define WCD939X_HPH_REFBUFF_UHQA_CTL (0x30dd) +#define WCD939X_REFBUFF_UHQA_CTL_SPARE_BITS GENMASK(7, 6) +#define WCD939X_REFBUFF_UHQA_CTL_HPH_VNEGREG2_COMP_CTL_OV BIT(5) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_RBIAS_ADJUST BIT(4) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFP_IOUT_CTL GENMASK(3, 2) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_IOUT_CTL GENMASK(1, 0) +#define WCD939X_HPH_REFBUFF_LP_CTL (0x30de) +#define WCD939X_REFBUFF_LP_CTL_HPH_VNEGREG2_CURR_COMP GENMASK(7, 6) +#define WCD939X_REFBUFF_LP_CTL_SPARE_BITS GENMASK(5, 4) +#define WCD939X_REFBUFF_LP_CTL_EN_PREREF_FILT_STARTUP_CLKDIV BIT(3) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_STARTUP_CLKDIV_CTL GENMASK(2, 1) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS BIT(0) +#define WCD939X_HPH_L_DAC_CTL (0x30df) +#define WCD939X_HPH_R_DAC_CTL (0x30e0) +#define WCD939X_HPH_SURGE_COMP_SEL (0x30e1) +#define WCD939X_HPH_SURGE_EN (0x30e2) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHL BIT(7) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHR BIT(6) +#define WCD939X_EN_SEL_SURGE_COMP_IQ GENMASK(5, 4) +#define WCD939X_EN_SURGE_VOLT_MODE_SHUTOFF_EN BIT(3) +#define WCD939X_EN_LATCH_INTR_OP_STG_HIZ_EN BIT(2) +#define WCD939X_EN_SURGE_LATCH_REG_RESET BIT(1) +#define WCD939X_EN_SWTICH_VN_VNDAC_NSURGE_EN BIT(0) +#define WCD939X_HPH_SURGE_MISC1 (0x30e3) +#define WCD939X_HPH_SURGE_STATUS (0x30e4) +#define WCD939X_EAR_EN (0x30e9) +#define WCD939X_EAR_PA_CON (0x30ea) +#define WCD939X_EAR_SP_CON (0x30eb) +#define WCD939X_EAR_DAC_CON (0x30ec) +#define WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL BIT(7) +#define WCD939X_DAC_CON_REF_DBG_EN BIT(6) +#define WCD939X_DAC_CON_REF_DBG_GAIN GENMASK(5, 3) +#define WCD939X_DAC_CON_GAIN_DAC GENMASK(2, 1) +#define WCD939X_DAC_CON_INV_DATA BIT(0) +#define WCD939X_EAR_CNP_FSM_CON (0x30ed) +#define WCD939X_EAR_TEST_CTL (0x30ee) +#define WCD939X_EAR_STATUS_REG_1 (0x30ef) +#define WCD939X_EAR_STATUS_REG_2 (0x30f0) +#define WCD939X_FLYBACK_NEW_CTRL_2 (0x30f6) +#define WCD939X_FLYBACK_NEW_CTRL_3 (0x30f7) +#define WCD939X_FLYBACK_NEW_CTRL_4 (0x30f8) +#define WCD939X_ANA_NEW_PAGE (0x3100) +#define WCD939X_HPH_NEW_ANA_HPH2 (0x3101) +#define WCD939X_HPH_NEW_ANA_HPH3 (0x3102) +#define WCD939X_SLEEP_CTL (0x3103) +#define WCD939X_SLEEP_WATCHDOG_CTL (0x3104) +#define WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311f) +#define WCD939X_MBHC_NEW_CTL_1 (0x3120) +#define WCD939X_CTL_1_RCO_EN BIT(7) +#define WCD939X_CTL_1_ADC_MODE BIT(4) +#define WCD939X_CTL_1_ADC_ENABLE BIT(3) +#define WCD939X_CTL_1_DETECTION_DONE BIT(2) +#define WCD939X_CTL_1_BTN_DBNC_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_CTL_2 (0x3121) +#define WCD939X_CTL_2_MUX_CTL GENMASK(6, 4) +#define WCD939X_CTL_2_M_RTH_CTL GENMASK(3, 2) +#define WCD939X_CTL_2_HS_VREF_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_PLUG_DETECT_CTL (0x3122) +#define WCD939X_MBHC_NEW_ZDET_ANA_CTL (0x3123) +#define WCD939X_ZDET_ANA_CTL_AVERAGING_EN BIT(7) +#define WCD939X_ZDET_ANA_CTL_MAXV_CTL GENMASK(6, 4) +#define WCD939X_ZDET_ANA_CTL_RANGE_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_ZDET_RAMP_CTL (0x3124) +#define WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL GENMASK(6, 4) +#define WCD939X_ZDET_RAMP_CTL_TIME_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_FSM_STATUS (0x3125) +#define WCD939X_FSM_STATUS_ADC_TIMEOUT BIT(7) +#define WCD939X_FSM_STATUS_ADC_COMPLETE BIT(6) +#define WCD939X_FSM_STATUS_HS_M_COMP_STATUS BIT(5) +#define WCD939X_FSM_STATUS_FAST_PRESS_FLAG_STATUS BIT(4) +#define WCD939X_FSM_STATUS_FAST_REMOVAL_FLAG_STATUS BIT(3) +#define WCD939X_FSM_STATUS_REMOVAL_FLAG_STATUS BIT(2) +#define WCD939X_FSM_STATUS_ELECT_REM_RT_STATUS BIT(1) +#define WCD939X_FSM_STATUS_BTN_STATUS BIT(0) +#define WCD939X_MBHC_NEW_ADC_RESULT (0x3126) +#define WCD939X_ADC_RESULT_VALUE GENMASK(7, 0) +#define WCD939X_TX_NEW_CH12_MUX (0x3127) +#define WCD939X_TX_NEW_CH34_MUX (0x3128) +#define WCD939X_DIE_CRACK_DET_EN (0x312c) +#define WCD939X_DIE_CRACK_DET_OUT (0x312d) +#define WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L (0x3133) +#define WCD939X_PA_GAIN_CTL_L_EN_HPHPA_2VPK BIT(7) +#define WCD939X_PA_GAIN_CTL_L_RX_SUPPLY_LEVEL BIT(6) +#define WCD939X_PA_GAIN_CTL_L_DAC_DR_BOOST BIT(5) +#define WCD939X_PA_GAIN_CTL_L_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134) +#define WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R (0x3136) +#define WCD939X_PA_GAIN_CTL_R_D_RCO_CLK_EN BIT(7) +#define WCD939X_PA_GAIN_CTL_R_SPARE_BITS GENMASK(6, 5) +#define WCD939X_PA_GAIN_CTL_R_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_PA_MISC1 (0x3137) +#define WCD939X_HPH_NEW_INT_PA_MISC2 (0x3138) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC (0x3139) +#define WCD939X_HPH_NEW_INT_TIMER1 (0x313a) +#define WCD939X_TIMER1_CURR_IDIV_CTL_CMPDR_OFF GENMASK(7, 5) +#define WCD939X_TIMER1_CURR_IDIV_CTL_AUTOCHOP GENMASK(4, 2) +#define WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN BIT(1) +#define WCD939X_HPH_NEW_INT_TIMER2 (0x313b) +#define WCD939X_HPH_NEW_INT_TIMER3 (0x313c) +#define WCD939X_HPH_NEW_INT_TIMER4 (0x313d) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313e) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313f) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3140) +#define WCD939X_RDAC_HD2_CTL_L_EN_HD2_RES_DIV_L BIT(7) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L GENMASK(5, 0) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3141) +#define WCD939X_RDAC_HD2_CTL_R_EN_HD2_RES_DIV_R BIT(7) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R GENMASK(5, 0) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146) +#define WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31af) +#define WCD939X_MOISTURE_DET_DC_CTRL_ONCOUNT GENMASK(6, 5) +#define WCD939X_MOISTURE_DET_DC_CTRL_OFFCOUNT GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31b0) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_HPHL_PA_EN BIT(6) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_DTEST_EN GENMASK(5, 4) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_OVRD_POLLING BIT(3) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING BIT(2) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_DBNC_TIME GENMASK(1, 0) +#define WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31b1) +#define WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW (0x31b2) +#define WCD939X_EAR_INT_NEW_CHOPPER_CON (0x31b7) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON1 (0x31b8) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON2 (0x31b9) +#define WCD939X_EAR_INT_NEW_DYNAMIC_BIAS (0x31ba) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_1 (0x31d0) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_2 (0x31d1) +#define WCD939X_DIE_CRACK_INT_DET_INT1 (0x31d3) +#define WCD939X_DIE_CRACK_INT_DET_INT2 (0x31d4) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2 (0x31d5) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1 (0x31d6) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0 (0x31d7) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M (0x31d8) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M (0x31d9) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1 (0x31da) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0 (0x31db) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP (0x31dc) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1 (0x31dd) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0 (0x31de) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP (0x31df) +#define WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE GENMASK(4, 0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0 (0x31e0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP (0x31e1) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M GENMASK(7, 4) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE GENMASK(3, 0) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1 (0x31e2) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP (0x31e3) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L2 (0x31e4) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L1 (0x31e5) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L0 (0x31e6) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_ULP (0x31e7) +#define WCD939X_DIGITAL_PAGE (0x3400) +#define WCD939X_DIGITAL_CHIP_ID0 (0x3401) +#define WCD939X_DIGITAL_CHIP_ID1 (0x3402) +#define WCD939X_DIGITAL_CHIP_ID2 (0x3403) +#define WCD939X_DIGITAL_CHIP_ID3 (0x3404) +#define WCD939X_DIGITAL_SWR_TX_CLK_RATE (0x3405) +#define WCD939X_DIGITAL_CDC_RST_CTL (0x3406) +#define WCD939X_DIGITAL_TOP_CLK_CFG (0x3407) +#define WCD939X_DIGITAL_CDC_ANA_CLK_CTL (0x3408) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV4_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN BIT(0) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_DIGITAL_CDC_DIG_CLK_CTL (0x3409) +#define WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN BIT(7) +#define WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN BIT(6) +#define WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN BIT(5) +#define WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN BIT(4) +#define WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN BIT(2) +#define WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN BIT(1) +#define WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN BIT(0) +#define WCD939X_DIGITAL_SWR_RST_EN (0x340a) +#define WCD939X_DIGITAL_CDC_PATH_MODE (0x340b) +#define WCD939X_DIGITAL_CDC_RX_RST (0x340c) +#define WCD939X_DIGITAL_CDC_RX0_CTL (0x340d) +#define WCD939X_DIGITAL_CDC_RX1_CTL (0x340e) +#define WCD939X_DIGITAL_CDC_RX2_CTL (0x340f) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_COMP_CTL_0 (0x3414) +#define WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN BIT(1) +#define WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN BIT(0) +#define WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_MBHC_1P2M_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX3_ADC_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX2_ADC_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX1_ADC_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX0_ADC_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN BIT(0) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341e) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341f) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R1 (0x3428) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R2 (0x3429) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R3 (0x342a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R4 (0x342b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R5 (0x342c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R6 (0x342d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R7 (0x342e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_0 (0x342f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_1 (0x3430) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_0 (0x3431) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_1 (0x3432) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_0 (0x3433) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_1 (0x3434) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_0 (0x3435) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_1 (0x3436) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_0 (0x3437) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_1 (0x3438) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A6_0 (0x3439) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A7_0 (0x343a) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_0 (0x343b) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_1 (0x343c) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_2 (0x343d) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_3 (0x343e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R1 (0x343f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R2 (0x3440) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R3 (0x3441) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R4 (0x3442) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R5 (0x3443) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R6 (0x3444) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R7 (0x3445) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344a) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0 (0x344b) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1 (0x344c) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2 (0x344d) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_CTL (0x344e) +#define WCD939X_CDC_HPH_GAIN_CTL_HPH_STEREO_EN BIT(4) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN BIT(3) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN BIT(2) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_DSD_EN BIT(1) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_DSD_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_CTL (0x344f) +#define WCD939X_CDC_EAR_GAIN_CTL_EAR_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_PATH_CTL (0x3450) +#define WCD939X_DIGITAL_CDC_SWR_CLH (0x3451) +#define WCD939X_CDC_SWR_CLH_CLH_CTL GENMASK(7, 0) +#define WCD939X_DIGITAL_SWR_CLH_BYP (0x3452) +#define WCD939X_DIGITAL_CDC_TX0_CTL (0x3453) +#define WCD939X_DIGITAL_CDC_TX1_CTL (0x3454) +#define WCD939X_DIGITAL_CDC_TX2_CTL (0x3455) +#define WCD939X_DIGITAL_CDC_TX_RST (0x3456) +#define WCD939X_DIGITAL_CDC_REQ_CTL (0x3457) +#define WCD939X_CDC_REQ_CTL_TX3_WIDE_BAND BIT(5) +#define WCD939X_CDC_REQ_CTL_TX2_WIDE_BAND BIT(4) +#define WCD939X_CDC_REQ_CTL_TX1_WIDE_BAND BIT(3) +#define WCD939X_CDC_REQ_CTL_TX0_WIDE_BAND BIT(2) +#define WCD939X_CDC_REQ_CTL_FS_RATE_4P8 BIT(1) +#define WCD939X_CDC_REQ_CTL_NO_NOTCH BIT(0) +#define WCD939X_DIGITAL_CDC_RST (0x3458) +#define WCD939X_DIGITAL_CDC_AMIC_CTL (0x345a) +#define WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL BIT(3) +#define WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL BIT(2) +#define WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL BIT(1) +#define WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC_CTL (0x345b) +#define WCD939X_CDC_DMIC_CTL_DMIC_LEGACY_SW_MODE BIT(3) +#define WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN BIT(2) +#define WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN BIT(1) +#define WCD939X_CDC_DMIC_CTL_SOFT_RESET BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC1_CTL (0x345c) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC2_CTL (0x345d) +#define WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN BIT(7) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC3_CTL (0x345e) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC4_CTL (0x345f) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_EFUSE_PRG_CTL (0x3460) +#define WCD939X_DIGITAL_EFUSE_CTL (0x3461) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL0 (0x3465) +#define WCD939X_PDM_WD_CTL0_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL0_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL0_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL1 (0x3466) +#define WCD939X_PDM_WD_CTL1_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL1_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL1_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL2 (0x3467) +#define WCD939X_DIGITAL_INTR_MODE (0x346a) +#define WCD939X_DIGITAL_INTR_MASK_0 (0x346b) +#define WCD939X_DIGITAL_INTR_MASK_1 (0x346c) +#define WCD939X_DIGITAL_INTR_MASK_2 (0x346d) +#define WCD939X_DIGITAL_INTR_STATUS_0 (0x346e) +#define WCD939X_DIGITAL_INTR_STATUS_1 (0x346f) +#define WCD939X_DIGITAL_INTR_STATUS_2 (0x3470) +#define WCD939X_DIGITAL_INTR_CLEAR_0 (0x3471) +#define WCD939X_DIGITAL_INTR_CLEAR_1 (0x3472) +#define WCD939X_DIGITAL_INTR_CLEAR_2 (0x3473) +#define WCD939X_DIGITAL_INTR_LEVEL_0 (0x3474) +#define WCD939X_DIGITAL_INTR_LEVEL_1 (0x3475) +#define WCD939X_DIGITAL_INTR_LEVEL_2 (0x3476) +#define WCD939X_DIGITAL_INTR_SET_0 (0x3477) +#define WCD939X_DIGITAL_INTR_SET_1 (0x3478) +#define WCD939X_DIGITAL_INTR_SET_2 (0x3479) +#define WCD939X_DIGITAL_INTR_TEST_0 (0x347a) +#define WCD939X_DIGITAL_INTR_TEST_1 (0x347b) +#define WCD939X_DIGITAL_INTR_TEST_2 (0x347c) +#define WCD939X_DIGITAL_TX_MODE_DBG_EN (0x347f) +#define WCD939X_DIGITAL_TX_MODE_DBG_0_1 (0x3480) +#define WCD939X_DIGITAL_TX_MODE_DBG_2_3 (0x3481) +#define WCD939X_DIGITAL_LB_IN_SEL_CTL (0x3482) +#define WCD939X_DIGITAL_LOOP_BACK_MODE (0x3483) +#define WCD939X_DIGITAL_SWR_DAC_TEST (0x3484) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489) +#define WCD939X_DIGITAL_SWR_HM_TEST_0 (0x348a) +#define WCD939X_DIGITAL_SWR_HM_TEST_1 (0x348b) +#define WCD939X_DIGITAL_PAD_CTL_SWR_0 (0x348c) +#define WCD939X_DIGITAL_PAD_CTL_SWR_1 (0x348d) +#define WCD939X_DIGITAL_I2C_CTL (0x348e) +#define WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348f) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491) +#define WCD939X_DIGITAL_EFUSE_T_DATA_0 (0x3492) +#define WCD939X_DIGITAL_EFUSE_T_DATA_1 (0x3493) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498) +#define WCD939X_DIGITAL_PAD_INP_DIS_0 (0x3499) +#define WCD939X_DIGITAL_PAD_INP_DIS_1 (0x349a) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_0 (0x349b) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_1 (0x349c) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_2 (0x349d) +#define WCD939X_DIGITAL_RX_DATA_EDGE_CTL (0x349e) +#define WCD939X_DIGITAL_TX_DATA_EDGE_CTL (0x349f) +#define WCD939X_DIGITAL_GPIO_MODE (0x34a0) +#define WCD939X_DIGITAL_PIN_CTL_OE (0x34a1) +#define WCD939X_DIGITAL_PIN_CTL_DATA_0 (0x34a2) +#define WCD939X_DIGITAL_PIN_CTL_DATA_1 (0x34a3) +#define WCD939X_DIGITAL_PIN_STATUS_0 (0x34a4) +#define WCD939X_DIGITAL_PIN_STATUS_1 (0x34a5) +#define WCD939X_DIGITAL_DIG_DEBUG_CTL (0x34a6) +#define WCD939X_DIGITAL_DIG_DEBUG_EN (0x34a7) +#define WCD939X_DIGITAL_ANA_CSR_DBG_ADD (0x34a8) +#define WCD939X_DIGITAL_ANA_CSR_DBG_CTL (0x34a9) +#define WCD939X_DIGITAL_SSP_DBG (0x34aa) +#define WCD939X_DIGITAL_MODE_STATUS_0 (0x34ab) +#define WCD939X_DIGITAL_MODE_STATUS_1 (0x34ac) +#define WCD939X_DIGITAL_SPARE_0 (0x34ad) +#define WCD939X_DIGITAL_SPARE_1 (0x34ae) +#define WCD939X_DIGITAL_SPARE_2 (0x34af) +#define WCD939X_DIGITAL_EFUSE_REG_0 (0x34b0) +#define WCD939X_EFUSE_REG_0_WCD939X_ID GENMASK(4, 1) +#define WCD939X_EFUSE_REG_0_EFUSE_BLOWN BIT(0) +#define WCD939X_DIGITAL_EFUSE_REG_1 (0x34b1) +#define WCD939X_DIGITAL_EFUSE_REG_2 (0x34b2) +#define WCD939X_DIGITAL_EFUSE_REG_3 (0x34b3) +#define WCD939X_DIGITAL_EFUSE_REG_4 (0x34b4) +#define WCD939X_DIGITAL_EFUSE_REG_5 (0x34b5) +#define WCD939X_DIGITAL_EFUSE_REG_6 (0x34b6) +#define WCD939X_DIGITAL_EFUSE_REG_7 (0x34b7) +#define WCD939X_DIGITAL_EFUSE_REG_8 (0x34b8) +#define WCD939X_DIGITAL_EFUSE_REG_9 (0x34b9) +#define WCD939X_DIGITAL_EFUSE_REG_10 (0x34ba) +#define WCD939X_DIGITAL_EFUSE_REG_11 (0x34bb) +#define WCD939X_DIGITAL_EFUSE_REG_12 (0x34bc) +#define WCD939X_DIGITAL_EFUSE_REG_13 (0x34bd) +#define WCD939X_DIGITAL_EFUSE_REG_14 (0x34be) +#define WCD939X_DIGITAL_EFUSE_REG_15 (0x34bf) +#define WCD939X_DIGITAL_EFUSE_REG_16 (0x34c0) +#define WCD939X_DIGITAL_EFUSE_REG_17 (0x34c1) +#define WCD939X_DIGITAL_EFUSE_REG_18 (0x34c2) +#define WCD939X_DIGITAL_EFUSE_REG_19 (0x34c3) +#define WCD939X_DIGITAL_EFUSE_REG_20 (0x34c4) +#define WCD939X_DIGITAL_EFUSE_REG_21 (0x34c5) +#define WCD939X_DIGITAL_EFUSE_REG_22 (0x34c6) +#define WCD939X_DIGITAL_EFUSE_REG_23 (0x34c7) +#define WCD939X_DIGITAL_EFUSE_REG_24 (0x34c8) +#define WCD939X_DIGITAL_EFUSE_REG_25 (0x34c9) +#define WCD939X_DIGITAL_EFUSE_REG_26 (0x34ca) +#define WCD939X_DIGITAL_EFUSE_REG_27 (0x34cb) +#define WCD939X_DIGITAL_EFUSE_REG_28 (0x34cc) +#define WCD939X_DIGITAL_EFUSE_REG_29 (0x34cd) +#define WCD939X_DIGITAL_EFUSE_REG_30 (0x34ce) +#define WCD939X_DIGITAL_EFUSE_REG_31 (0x34cf) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_0 (0x34d0) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_1 (0x34d1) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_2 (0x34d2) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_3 (0x34d3) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_4 (0x34d4) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA0 (0x34d5) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA1 (0x34d6) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA2 (0x34d7) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA3 (0x34d8) +#define WCD939X_DIGITAL_DEM_SECOND_ORDER (0x34d9) +#define WCD939X_DIGITAL_DSM_CTRL (0x34da) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_0 (0x34db) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_1 (0x34dc) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_2 (0x34dd) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_3 (0x34de) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_0 (0x34df) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_1 (0x34e0) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_2 (0x34e1) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_3 (0x34e2) +#define WCD939X_RX_TOP_PAGE (0x3500) +#define WCD939X_RX_TOP_TOP_CFG0 (0x3501) +#define WCD939X_TOP_CFG0_HPH_DAC_RATE_SEL BIT(1) +#define WCD939X_TOP_CFG0_PGA_UPDATE BIT(0) +#define WCD939X_RX_TOP_HPHL_COMP_WR_LSB (0x3502) +#define WCD939X_RX_TOP_HPHL_COMP_WR_MSB (0x3503) +#define WCD939X_RX_TOP_HPHL_COMP_LUT (0x3504) +#define WCD939X_RX_TOP_HPHL_COMP_RD_LSB (0x3505) +#define WCD939X_RX_TOP_HPHL_COMP_RD_MSB (0x3506) +#define WCD939X_RX_TOP_HPHR_COMP_WR_LSB (0x3507) +#define WCD939X_RX_TOP_HPHR_COMP_WR_MSB (0x3508) +#define WCD939X_RX_TOP_HPHR_COMP_LUT (0x3509) +#define WCD939X_RX_TOP_HPHR_COMP_RD_LSB (0x350a) +#define WCD939X_RX_TOP_HPHR_COMP_RD_MSB (0x350b) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG1 (0x350c) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG2 (0x350d) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG3 (0x350e) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG4 (0x350f) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG5 (0x3510) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG6 (0x3511) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG1 (0x3512) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG2 (0x3513) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG3 (0x3514) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG4 (0x3515) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG5 (0x3516) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG6 (0x3517) +#define WCD939X_RX_TOP_HPHL_PATH_CFG0 (0x351c) +#define WCD939X_HPHL_PATH_CFG0_INT_EN BIT(1) +#define WCD939X_HPHL_PATH_CFG0_DLY_ZN_EN BIT(0) +#define WCD939X_RX_TOP_HPHL_PATH_CFG1 (0x351d) +#define WCD939X_HPHL_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHL_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHL_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHL_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHL_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_HPHR_PATH_CFG0 (0x351e) +#define WCD939X_HPHR_PATH_CFG0_INT_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG0_DLY_ZN_EN BIT(1) +#define WCD939X_RX_TOP_HPHR_PATH_CFG1 (0x351f) +#define WCD939X_HPHR_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHR_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHR_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHR_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_PATH_CFG2 (0x3520) +#define WCD939X_RX_TOP_HPHL_PATH_SEC0 (0x3521) +#define WCD939X_RX_TOP_HPHL_PATH_SEC1 (0x3522) +#define WCD939X_RX_TOP_HPHL_PATH_SEC2 (0x3523) +#define WCD939X_RX_TOP_HPHL_PATH_SEC3 (0x3524) +#define WCD939X_RX_TOP_HPHR_PATH_SEC0 (0x3525) +#define WCD939X_RX_TOP_HPHR_PATH_SEC1 (0x3526) +#define WCD939X_RX_TOP_HPHR_PATH_SEC2 (0x3527) +#define WCD939X_RX_TOP_HPHR_PATH_SEC3 (0x3528) +#define WCD939X_RX_TOP_PATH_SEC4 (0x3529) +#define WCD939X_RX_TOP_PATH_SEC5 (0x352a) +#define WCD939X_COMPANDER_HPHL_CTL0 (0x3540) +#define WCD939X_COMPANDER_HPHL_CTL1 (0x3541) +#define WCD939X_COMPANDER_HPHL_CTL2 (0x3542) +#define WCD939X_COMPANDER_HPHL_CTL3 (0x3543) +#define WCD939X_COMPANDER_HPHL_CTL4 (0x3544) +#define WCD939X_COMPANDER_HPHL_CTL5 (0x3545) +#define WCD939X_COMPANDER_HPHL_CTL6 (0x3546) +#define WCD939X_COMPANDER_HPHL_CTL7 (0x3547) +#define WCD939X_COMPANDER_HPHL_CTL8 (0x3548) +#define WCD939X_COMPANDER_HPHL_CTL9 (0x3549) +#define WCD939X_COMPANDER_HPHL_CTL10 (0x354a) +#define WCD939X_COMPANDER_HPHL_CTL11 (0x354b) +#define WCD939X_COMPANDER_HPHL_CTL12 (0x354c) +#define WCD939X_COMPANDER_HPHL_CTL13 (0x354d) +#define WCD939X_COMPANDER_HPHL_CTL14 (0x354e) +#define WCD939X_COMPANDER_HPHL_CTL15 (0x354f) +#define WCD939X_COMPANDER_HPHL_CTL16 (0x3550) +#define WCD939X_COMPANDER_HPHL_CTL17 (0x3551) +#define WCD939X_COMPANDER_HPHL_CTL18 (0x3552) +#define WCD939X_COMPANDER_HPHL_CTL19 (0x3553) +#define WCD939X_R_CTL0 (0x3560) +#define WCD939X_R_CTL1 (0x3561) +#define WCD939X_R_CTL2 (0x3562) +#define WCD939X_R_CTL3 (0x3563) +#define WCD939X_R_CTL4 (0x3564) +#define WCD939X_R_CTL5 (0x3565) +#define WCD939X_R_CTL6 (0x3566) +#define WCD939X_R_CTL7 (0x3567) +#define WCD939X_R_CTL8 (0x3568) +#define WCD939X_R_CTL9 (0x3569) +#define WCD939X_R_CTL10 (0x356a) +#define WCD939X_R_CTL11 (0x356b) +#define WCD939X_R_CTL12 (0x356c) +#define WCD939X_R_CTL13 (0x356d) +#define WCD939X_R_CTL14 (0x356e) +#define WCD939X_R_CTL15 (0x356f) +#define WCD939X_R_CTL16 (0x3570) +#define WCD939X_R_CTL17 (0x3571) +#define WCD939X_R_CTL18 (0x3572) +#define WCD939X_R_CTL19 (0x3573) +#define WCD939X_E_PATH_CTL (0x3580) +#define WCD939X_E_CFG0 (0x3581) +#define WCD939X_CFG0_AUTO_DISABLE_ANC BIT(2) +#define WCD939X_CFG0_AUTO_DISABLE_DSD BIT(1) +#define WCD939X_CFG0_IDLE_STEREO BIT(0) +#define WCD939X_E_CFG1 (0x3582) +#define WCD939X_E_CFG2 (0x3583) +#define WCD939X_E_CFG3 (0x3584) +#define WCD939X_DSD_HPHL_PATH_CTL (0x3590) +#define WCD939X_DSD_HPHL_CFG0 (0x3591) +#define WCD939X_DSD_HPHL_CFG1 (0x3592) +#define WCD939X_DSD_HPHL_CFG2 (0x3593) +#define WCD939X_DSD_HPHL_CFG3 (0x3594) +#define WCD939X_DSD_HPHL_CFG4 (0x3595) +#define WCD939X_DSD_HPHL_CFG5 (0x3596) +#define WCD939X_DSD_HPHR_PATH_CTL (0x35a0) +#define WCD939X_DSD_HPHR_CFG0 (0x35a1) +#define WCD939X_DSD_HPHR_CFG1 (0x35a2) +#define WCD939X_DSD_HPHR_CFG2 (0x35a3) +#define WCD939X_DSD_HPHR_CFG3 (0x35a4) +#define WCD939X_DSD_HPHR_CFG4 (0x35a5) +#define WCD939X_DSD_HPHR_CFG5 (0x35a6) +#define WCD939X_MAX_REGISTER (WCD939X_DSD_HPHR_CFG5) + +#define WCD939X_MAX_SWR_PORTS (6) +#define WCD939X_MAX_RX_SWR_PORTS (6) +#define WCD939X_MAX_TX_SWR_PORTS (4) +#define WCD939X_MAX_SWR_CH_IDS (15) + +struct wcd939x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +enum wcd939x_tx_sdw_ports { + WCD939X_ADC_1_4_PORT = 1, + WCD939X_ADC_DMIC_1_2_PORT, + WCD939X_DMIC_0_3_MBHC_PORT, + WCD939X_DMIC_3_7_PORT, +}; + +enum wcd939x_tx_sdw_channels { + WCD939X_ADC1, + WCD939X_ADC2, + WCD939X_ADC3, + WCD939X_ADC4, + WCD939X_DMIC0, + WCD939X_DMIC1, + WCD939X_MBHC, + WCD939X_DMIC2, + WCD939X_DMIC3, + WCD939X_DMIC4, + WCD939X_DMIC5, + WCD939X_DMIC6, + WCD939X_DMIC7, +}; + +enum wcd939x_rx_sdw_ports { + WCD939X_HPH_PORT = 1, + WCD939X_CLSH_PORT, + WCD939X_COMP_PORT, + WCD939X_LO_PORT, + WCD939X_DSD_PORT, + WCD939X_HIFI_PCM_PORT, +}; + +enum wcd939x_rx_sdw_channels { + WCD939X_HPH_L, + WCD939X_HPH_R, + WCD939X_CLSH, + WCD939X_COMP_L, + WCD939X_COMP_R, + WCD939X_LO, + WCD939X_DSD_L, + WCD939X_DSD_R, + WCD939X_HIFI_PCM_L, + WCD939X_HIFI_PCM_R, +}; + +enum { + WCD939X_SDW_DIR_RX, + WCD939X_SDW_DIR_TX, +}; + +struct wcd939x_priv; +struct wcd939x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + struct wcd939x_sdw_ch_info *ch_info; + bool port_enable[WCD939X_MAX_SWR_CH_IDS]; + int active_ports; + int num_ports; + bool is_tx; + struct wcd939x_priv *wcd939x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD939X_SDW) +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd939x_sdw_device_get(struct device_node *np); +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd); +#else + +static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return NULL; +} + +static inline unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return 0; +} + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + return PTR_ERR(-EINVAL); +} +#endif /* CONFIG_SND_SOC_WCD939X_SDW */ + +#endif /* __WCD939X_H__ */ diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 36ea0dcdc7ab..e451c009f2d9 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1092,27 +1092,36 @@ static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) dsp->fatal_error = false; } +int wm_adsp_run(struct wm_adsp *dsp) +{ + flush_work(&dsp->boot_work); + + return cs_dsp_run(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_run); + +void wm_adsp_stop(struct wm_adsp *dsp) +{ + cs_dsp_stop(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_stop); + int wm_adsp_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 wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - int ret = 0; switch (event) { case SND_SOC_DAPM_POST_PMU: - flush_work(&dsp->boot_work); - ret = cs_dsp_run(&dsp->cs_dsp); - break; + return wm_adsp_run(dsp); case SND_SOC_DAPM_PRE_PMD: - cs_dsp_stop(&dsp->cs_dsp); - break; + wm_adsp_stop(dsp); + return 0; default: - break; + return 0; } - - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_event); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 067d807a7ca8..e53dfcf1f78f 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -98,6 +98,8 @@ irqreturn_t wm_adsp2_bus_error(int irq, void *data); irqreturn_t wm_halo_bus_error(int irq, void *data); irqreturn_t wm_halo_wdt_expire(int irq, void *data); +int wm_adsp_run(struct wm_adsp *dsp); +void wm_adsp_stop(struct wm_adsp *dsp); int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index f2653df84e4a..a9767ef0e39d 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -13,6 +13,7 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> @@ -699,6 +700,7 @@ struct wsa884x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; + struct reset_control *sd_reset; bool port_prepared[WSA884X_MAX_SWR_PORTS]; bool port_enable[WSA884X_MAX_SWR_PORTS]; unsigned int variant; @@ -1799,9 +1801,22 @@ static struct snd_soc_dai_driver wsa884x_dais[] = { }, }; -static void wsa884x_gpio_powerdown(void *data) +static void wsa884x_reset_powerdown(void *data) { - gpiod_direction_output(data, 1); + struct wsa884x_priv *wsa884x = data; + + if (wsa884x->sd_reset) + reset_control_assert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 1); +} + +static void wsa884x_reset_deassert(struct wsa884x_priv *wsa884x) +{ + if (wsa884x->sd_reset) + reset_control_deassert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 0); } static void wsa884x_regulator_disable(void *data) @@ -1809,6 +1824,27 @@ static void wsa884x_regulator_disable(void *data) regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); } +static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x) +{ + wsa884x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa884x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_reset), + "Failed to get reset\n"); + else if (wsa884x->sd_reset) + return 0; + /* + * else: NULL, so use the backwards compatible way for powerdown-gpios, + * which does not handle sharing GPIO properly. + */ + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + return 0; +} + static int wsa884x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1838,11 +1874,9 @@ static int wsa884x_probe(struct sdw_slave *pdev, if (ret) return ret; - wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", - GPIOD_OUT_HIGH); - if (IS_ERR(wsa884x->sd_n)) - return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), - "Shutdown Control GPIO not found\n"); + ret = wsa884x_get_reset(dev, wsa884x); + if (ret) + return ret; dev_set_drvdata(dev, wsa884x); wsa884x->slave = pdev; @@ -1858,9 +1892,8 @@ static int wsa884x_probe(struct sdw_slave *pdev, pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - /* Bring out of reset */ - gpiod_direction_output(wsa884x->sd_n, 0); - ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + wsa884x_reset_deassert(wsa884x); + ret = devm_add_action_or_reset(dev, wsa884x_reset_powerdown, wsa884x); if (ret) return ret; diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 63f1f05da947..6be074ea0b3f 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -196,7 +196,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) } } - ret = snd_soc_register_card(&eukrea_tlv320); + ret = devm_snd_soc_register_card(&pdev->dev, &eukrea_tlv320); err: if (ret) dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -205,11 +205,6 @@ err: return ret; } -static void eukrea_tlv320_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&eukrea_tlv320); -} - static const struct of_device_id imx_tlv320_dt_ids[] = { { .compatible = "eukrea,asoc-tlv320"}, { /* sentinel */ } @@ -222,7 +217,6 @@ static struct platform_driver eukrea_tlv320_driver = { .of_match_table = imx_tlv320_dt_ids, }, .probe = eukrea_tlv320_probe, - .remove_new = eukrea_tlv320_remove, }; module_platform_driver(eukrea_tlv320_driver); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 546bd4e333b5..0e2c31439670 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1639,6 +1639,18 @@ static const struct fsl_sai_soc_data fsl_sai_imx93_data = { .max_burst = {8, 8}, }; +static const struct fsl_sai_soc_data fsl_sai_imx95_data = { + .use_imx_pcm = true, + .use_edma = true, + .fifo_depth = 128, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 8, + .flags = 0, + .max_register = FSL_SAI_MCTL, + .max_burst = {8, 8}, +}; + static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data }, { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data }, @@ -1651,6 +1663,7 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data }, { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data }, { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data }, + { .compatible = "fsl,imx95-sai", .data = &fsl_sai_imx95_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index 18d129c21648..a42149311c8b 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -61,7 +61,7 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts, /* There's only one global utilities register */ static phys_addr_t guts_phys; -/** +/* * machine_data: machine-specific ASoC device data * * This structure contains data for a single sound platform device on an @@ -80,11 +80,14 @@ struct machine_data { }; /** - * p1022_rdk_machine_probe: initialize the board + * p1022_rdk_machine_probe - initialize the board + * @card: ASoC card instance * * This function is used to initialize the board-specific hardware. * * Here we program the DMACR and PMUXCR registers. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_machine_probe(struct snd_soc_card *card) { @@ -119,11 +122,14 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) } /** - * p1022_rdk_startup: program the board with various hardware parameters + * p1022_rdk_startup - program the board with various hardware parameters + * @substream: ASoC substream object * * This function takes board-specific information, like clock frequencies * and serial data formats, and passes that information to the codec and * transport drivers. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_startup(struct snd_pcm_substream *substream) { @@ -153,10 +159,13 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) } /** - * p1022_rdk_machine_remove: Remove the sound device + * p1022_rdk_machine_remove - Remove the sound device + * @card: ASoC card instance * * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_machine_remove(struct snd_soc_card *card) { @@ -181,7 +190,7 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card) return 0; } -/** +/* * p1022_rdk_ops: ASoC machine driver operations */ static const struct snd_soc_ops p1022_rdk_ops = { @@ -189,11 +198,14 @@ static const struct snd_soc_ops p1022_rdk_ops = { }; /** - * p1022_rdk_probe: platform probe function for the machine driver + * p1022_rdk_probe - platform probe function for the machine driver + * @pdev: platform device pointer * * Although this is a machine driver, the SSI node is the "master" node with * respect to audio hardware connections. Therefore, we create a new ASoC * device for each new SSI node that has a codec attached. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_probe(struct platform_device *pdev) { @@ -341,7 +353,8 @@ error_put: } /** - * p1022_rdk_remove: remove the platform device + * p1022_rdk_remove - remove the platform device + * @pdev: platform device pointer * * This function is called when the platform device is removed. */ @@ -368,9 +381,11 @@ static struct platform_driver p1022_rdk_driver = { }; /** - * p1022_rdk_init: machine driver initialization. + * p1022_rdk_init - machine driver initialization. * * This function is called when this module is loaded. + * + * Returns: %0 on success or negative errno value on error */ static int __init p1022_rdk_init(void) { @@ -391,7 +406,7 @@ static int __init p1022_rdk_init(void) } /** - * p1022_rdk_exit: machine driver exit + * p1022_rdk_exit - machine driver exit * * This function is called when this driver is unloaded. */ diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 3fc2c9a6c44d..0630e58b9d6b 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -19,8 +19,9 @@ #include <sound/pcm.h> #include <sound/soc.h> #include <sound/compress_driver.h> -#include <asm/intel-mid.h> + #include <asm/platform_sst_audio.h> + #include "../sst-mfld-platform.h" #include "sst.h" diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 460ee6599daf..5480500337f8 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ - topology.o path.o pcm.o board_selection.o control.o + topology.o path.o pcm.o board_selection.o control.o \ + sysfs.o snd-soc-avs-objs += cldma.o -snd-soc-avs-objs += skl.o apl.o +snd-soc-avs-objs += skl.o apl.o cnl.o icl.o tgl.o snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 1860099c782a..c21ecaef9eba 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -13,11 +13,11 @@ #include "path.h" #include "topology.h" -static int __maybe_unused -apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +#ifdef CONFIG_DEBUG_FS +int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { - struct apl_log_state_info *info; + struct avs_apl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; int ret, i; @@ -47,10 +47,11 @@ apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri return 0; } +#endif -static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { - struct apl_log_buffer_layout layout; + struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; addr = avs_log_buffer_addr(adev, msg->log.core); @@ -63,11 +64,11 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg /* consume the logs regardless of consumer presence */ goto update_read_ptr; - buf = apl_log_payload_addr(addr); + buf = avs_apl_log_payload_addr(addr); if (layout.read_ptr > layout.write_ptr) { avs_dump_fw_log(adev, buf + layout.read_ptr, - apl_log_payload_size(adev) - layout.read_ptr); + avs_apl_log_payload_size(adev) - layout.read_ptr); layout.read_ptr = 0; } avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr); @@ -77,7 +78,8 @@ update_read_ptr: return 0; } -static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout) +static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core, + struct avs_apl_log_buffer_layout *layout) { unsigned long timeout; void __iomem *addr; @@ -99,11 +101,11 @@ static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buf } /* reads log header and tests its type */ -#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) +#define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) -static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) { - struct apl_log_buffer_layout layout; + struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; size_t dump_size; u16 offset = 0; @@ -124,9 +126,9 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) if (!addr) goto exit; - buf = apl_log_payload_addr(addr); + buf = avs_apl_log_payload_addr(addr); memcpy_fromio(&layout, addr, sizeof(layout)); - if (!apl_is_entry_stackdump(buf + layout.read_ptr)) { + if (!avs_apl_is_entry_stackdump(buf + layout.read_ptr)) { union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); /* @@ -142,11 +144,11 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) do { u32 count; - if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout)) + if (avs_apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout)) break; if (layout.read_ptr > layout.write_ptr) { - count = apl_log_payload_size(adev) - layout.read_ptr; + count = avs_apl_log_payload_size(adev) - layout.read_ptr; memcpy_fromio(pos + offset, buf + layout.read_ptr, count); layout.read_ptr = 0; offset += count; @@ -165,7 +167,7 @@ exit: return 0; } -static bool apl_lp_streaming(struct avs_dev *adev) +static bool avs_apl_lp_streaming(struct avs_dev *adev) { struct avs_path *path; @@ -201,7 +203,7 @@ static bool apl_lp_streaming(struct avs_dev *adev) return true; } -static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { /* wake in all cases */ if (wake) @@ -215,10 +217,10 @@ static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool w * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition, * not the power-gating mechanism known from cAVS 2.0. */ - return apl_lp_streaming(adev); + return avs_apl_lp_streaming(adev); } -static int apl_set_d0ix(struct avs_dev *adev, bool enable) +int avs_apl_set_d0ix(struct avs_dev *adev, bool enable) { bool streaming = false; int ret; @@ -231,20 +233,20 @@ static int apl_set_d0ix(struct avs_dev *adev, bool enable) return AVS_IPC_RET(ret); } -const struct avs_dsp_ops apl_dsp_ops = { +const struct avs_dsp_ops avs_apl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_dsp_irq_handler, - .irq_thread = avs_dsp_irq_thread, + .irq_handler = avs_irq_handler, + .irq_thread = avs_skl_irq_thread, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, - .log_buffer_offset = skl_log_buffer_offset, - .log_buffer_status = apl_log_buffer_status, - .coredump = apl_coredump, - .d0ix_toggle = apl_d0ix_toggle, - .set_d0ix = apl_set_d0ix, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_apl_d0ix_toggle, + .set_d0ix = avs_apl_set_d0ix, AVS_SET_ENABLE_LOGS_OP(apl) }; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index d694e08e44e1..f80f79415344 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -46,8 +46,8 @@ struct avs_dsp_ops { int (* const power)(struct avs_dev *, u32, bool); int (* const reset)(struct avs_dev *, u32, bool); int (* const stall)(struct avs_dev *, u32, bool); - irqreturn_t (* const irq_handler)(int, void *); - irqreturn_t (* const irq_thread)(int, void *); + irqreturn_t (* const irq_handler)(struct avs_dev *); + irqreturn_t (* const irq_thread)(struct avs_dev *); void (* const int_control)(struct avs_dev *, bool); int (* const load_basefw)(struct avs_dev *, struct firmware *); int (* const load_lib)(struct avs_dev *, struct firmware *, u32); @@ -64,8 +64,11 @@ struct avs_dsp_ops { #define avs_dsp_op(adev, op, ...) \ ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) -extern const struct avs_dsp_ops skl_dsp_ops; -extern const struct avs_dsp_ops apl_dsp_ops; +extern const struct avs_dsp_ops avs_skl_dsp_ops; +extern const struct avs_dsp_ops avs_apl_dsp_ops; +extern const struct avs_dsp_ops avs_cnl_dsp_ops; +extern const struct avs_dsp_ops avs_icl_dsp_ops; +extern const struct avs_dsp_ops avs_tgl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) @@ -73,6 +76,23 @@ extern const struct avs_dsp_ops apl_dsp_ops; #define avs_platattr_test(adev, attr) \ ((adev)->spec->attributes & AVS_PLATATTR_##attr) +struct avs_sram_spec { + const u32 base_offset; + const u32 window_size; + const u32 rom_status_offset; +}; + +struct avs_hipc_spec { + const u32 req_offset; + const u32 req_ext_offset; + const u32 req_busy_mask; + const u32 ack_offset; + const u32 ack_done_mask; + const u32 rsp_offset; + const u32 rsp_busy_mask; + const u32 ctl_offset; +}; + /* Platform specific descriptor */ struct avs_spec { const char *name; @@ -82,9 +102,8 @@ struct avs_spec { const u32 core_init_mask; /* used during DSP boot */ const u64 attributes; /* bitmask of AVS_PLATATTR_* */ - const u32 sram_base_offset; - const u32 sram_window_size; - const u32 rom_status; + const struct avs_sram_spec *sram; + const struct avs_hipc_spec *hipc; }; struct avs_fw_entry { @@ -127,6 +146,7 @@ struct avs_dev { int *core_refs; /* reference count per core */ char **lib_names; int num_lp_paths; + atomic_t l1sen_counter; /* controls whether L1SEN should be disabled */ struct completion fw_ready; struct work_struct probe_work; @@ -225,8 +245,7 @@ struct avs_ipc { #define AVS_IPC_RET(ret) \ (((ret) <= 0) ? (ret) : -AVS_EIPC) -irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id); -irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id); +irqreturn_t avs_irq_handler(struct avs_dev *adev); void avs_dsp_process_response(struct avs_dev *adev, u64 header); int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, struct avs_ipc_msg *reply, int timeout, const char *name); @@ -248,7 +267,20 @@ void avs_ipc_block(struct avs_ipc *ipc); int avs_dsp_disable_d0ix(struct avs_dev *adev); int avs_dsp_enable_d0ix(struct avs_dev *adev); -int skl_log_buffer_offset(struct avs_dev *adev, u32 core); +irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); +irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev); +int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); +int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); +int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core); +int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core); +int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg); +int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg); +bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake); +bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake); +int avs_apl_set_d0ix(struct avs_dev *adev, bool enable); +int avs_icl_set_d0ix(struct avs_dev *adev, bool enable); /* Firmware resources management */ @@ -293,6 +325,8 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); int avs_hda_transfer_modules(struct avs_dev *adev, bool load, struct avs_module_entry *mods, u32 num_mods); +int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); + /* Soc component members */ struct avs_soc_component { @@ -342,21 +376,21 @@ static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_n return ret; } -struct apl_log_buffer_layout { +struct avs_apl_log_buffer_layout { u32 read_ptr; u32 write_ptr; u8 buffer[]; } __packed; -#define apl_log_payload_size(adev) \ - (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout)) +#define avs_apl_log_payload_size(adev) \ + (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout)) -#define apl_log_payload_addr(addr) \ - (addr + sizeof(struct apl_log_buffer_layout)) +#define avs_apl_log_payload_addr(addr) \ + (addr + sizeof(struct avs_apl_log_buffer_layout)) #ifdef CONFIG_DEBUG_FS #define AVS_SET_ENABLE_LOGS_OP(name) \ - .enable_logs = name##_enable_logs + .enable_logs = avs_##name##_enable_logs bool avs_logging_fw(struct avs_dev *adev); void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len); @@ -392,4 +426,8 @@ static inline void avs_debugfs_init(struct avs_dev *adev) { } static inline void avs_debugfs_exit(struct avs_dev *adev) { } #endif +/* Filesystems integration */ + +extern const struct attribute_group *avs_attr_groups[]; + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 8e91eece992d..8360ce557401 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -236,6 +236,82 @@ static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = { {}, }; +static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC5682", + .drv_name = "avs_rt5682", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5682-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC0298", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "10EC1308", + .drv_name = "avs_rt1308", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt1308-tplg.bin", + }, + { + .id = "ESSX8336", + .drv_name = "avs_es8336", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "es8336-tplg.bin", + }, + {}, +}; + static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { { .drv_name = "avs_i2s_test", @@ -296,6 +372,15 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines), AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines), AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), + AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), {}, }; diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c new file mode 100644 index 000000000000..5423c29ecc4e --- /dev/null +++ b/sound/soc/intel/avs/cnl.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "messages.h" + +irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev) +{ + union avs_reply_msg msg; + u32 hipctdr, hipctdd, hipctda; + + hipctdr = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR); + hipctdd = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD); + + /* Ensure DSP sent new response to process. */ + if (!(hipctdr & CNL_ADSP_HIPCTDR_BUSY)) + return IRQ_NONE; + + msg.primary = hipctdr; + msg.ext.val = hipctdd; + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR, + CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY); + /* Ack this response. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA, + CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE); + /* HW might have been clock gated, give some time for change to propagate. */ + snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda, + !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000); + /* Unmask busy interrupt. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); + + return IRQ_HANDLED; +} + +const struct avs_dsp_ops avs_cnl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_apl_d0ix_toggle, + .set_d0ix = avs_apl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(apl) +}; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index db78eb2f0108..d7f8940099ce 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -69,9 +69,14 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) { - u32 value = enable ? AZX_VS_EM2_L1SEN : 0; - - snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value); + if (enable) { + if (atomic_inc_and_test(&adev->l1sen_counter)) + snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, + AZX_VS_EM2_L1SEN); + } else { + if (atomic_dec_return(&adev->l1sen_counter) == -1) + snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, 0); + } } static int avs_hdac_bus_init_streams(struct hdac_bus *bus) @@ -144,7 +149,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) /* configure effectively creates new ASoC component */ ret = snd_hda_codec_configure(codec); if (ret < 0) { - dev_err(bus->dev, "failed to config codec %d\n", ret); + dev_warn(bus->dev, "failed to config codec #%d: %d\n", addr, ret); return ret; } @@ -153,15 +158,16 @@ static int probe_codec(struct hdac_bus *bus, int addr) static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus) { - int c; + int ret, c; /* First try to probe all given codec slots */ for (c = 0; c < HDA_MAX_CODECS; c++) { if (!(bus->codec_mask & BIT(c))) continue; - if (!probe_codec(bus, c)) - /* success, continue probing */ + ret = probe_codec(bus, c); + /* Ignore codecs with no supporting driver. */ + if (!ret || ret == -ENODEV) continue; /* @@ -316,6 +322,20 @@ static irqreturn_t hdac_bus_irq_thread(int irq, void *context) return IRQ_HANDLED; } +static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + + return avs_dsp_op(adev, irq_handler); +} + +static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + + return avs_dsp_op(adev, irq_thread); +} + static int avs_hdac_acquire_irq(struct avs_dev *adev) { struct hdac_bus *bus = &adev->base.core; @@ -725,38 +745,107 @@ static const struct dev_pm_ops avs_dev_pm = { SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) }; +static const struct avs_sram_spec skl_sram_spec = { + .base_offset = SKL_ADSP_SRAM_BASE_OFFSET, + .window_size = SKL_ADSP_SRAM_WINDOW_SIZE, + .rom_status_offset = SKL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_sram_spec apl_sram_spec = { + .base_offset = APL_ADSP_SRAM_BASE_OFFSET, + .window_size = APL_ADSP_SRAM_WINDOW_SIZE, + .rom_status_offset = APL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_hipc_spec skl_hipc_spec = { + .req_offset = SKL_ADSP_REG_HIPCI, + .req_ext_offset = SKL_ADSP_REG_HIPCIE, + .req_busy_mask = SKL_ADSP_HIPCI_BUSY, + .ack_offset = SKL_ADSP_REG_HIPCIE, + .ack_done_mask = SKL_ADSP_HIPCIE_DONE, + .rsp_offset = SKL_ADSP_REG_HIPCT, + .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY, + .ctl_offset = SKL_ADSP_REG_HIPCCTL, +}; + +static const struct avs_hipc_spec cnl_hipc_spec = { + .req_offset = CNL_ADSP_REG_HIPCIDR, + .req_ext_offset = CNL_ADSP_REG_HIPCIDD, + .req_busy_mask = CNL_ADSP_HIPCIDR_BUSY, + .ack_offset = CNL_ADSP_REG_HIPCIDA, + .ack_done_mask = CNL_ADSP_HIPCIDA_DONE, + .rsp_offset = CNL_ADSP_REG_HIPCTDR, + .rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY, + .ctl_offset = CNL_ADSP_REG_HIPCCTL, +}; + static const struct avs_spec skl_desc = { .name = "skl", - .min_fw_version = { - .major = 9, - .minor = 21, - .hotfix = 0, - .build = 4732, - }, - .dsp_ops = &skl_dsp_ops, + .min_fw_version = { 9, 21, 0, 4732 }, + .dsp_ops = &avs_skl_dsp_ops, .core_init_mask = 1, .attributes = AVS_PLATATTR_CLDMA, - .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET, - .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE, - .rom_status = SKL_ADSP_SRAM_BASE_OFFSET, + .sram = &skl_sram_spec, + .hipc = &skl_hipc_spec, }; static const struct avs_spec apl_desc = { .name = "apl", - .min_fw_version = { - .major = 9, - .minor = 22, - .hotfix = 1, - .build = 4323, - }, - .dsp_ops = &apl_dsp_ops, + .min_fw_version = { 9, 22, 1, 4323 }, + .dsp_ops = &avs_apl_dsp_ops, .core_init_mask = 3, .attributes = AVS_PLATATTR_IMR, - .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET, - .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE, - .rom_status = APL_ADSP_SRAM_BASE_OFFSET, + .sram = &apl_sram_spec, + .hipc = &skl_hipc_spec, +}; + +static const struct avs_spec cnl_desc = { + .name = "cnl", + .min_fw_version = { 10, 23, 0, 5314 }, + .dsp_ops = &avs_cnl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, +}; + +static const struct avs_spec icl_desc = { + .name = "icl", + .min_fw_version = { 10, 23, 0, 5040 }, + .dsp_ops = &avs_icl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, }; +static const struct avs_spec jsl_desc = { + .name = "jsl", + .min_fw_version = { 10, 26, 0, 5872 }, + .dsp_ops = &avs_icl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, +}; + +#define AVS_TGL_BASED_SPEC(sname) \ +static const struct avs_spec sname##_desc = { \ + .name = #sname, \ + .min_fw_version = { 10, 29, 0, 5646 }, \ + .dsp_ops = &avs_tgl_dsp_ops, \ + .core_init_mask = 1, \ + .attributes = AVS_PLATATTR_IMR, \ + .sram = &apl_sram_spec, \ + .hipc = &cnl_hipc_spec, \ +} + +AVS_TGL_BASED_SPEC(lkf); +AVS_TGL_BASED_SPEC(tgl); +AVS_TGL_BASED_SPEC(ehl); +AVS_TGL_BASED_SPEC(adl); +AVS_TGL_BASED_SPEC(adl_n); + static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) }, @@ -766,6 +855,32 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_LKF, &lkf_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_R, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &adl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); @@ -776,6 +891,7 @@ static struct pci_driver avs_pci_driver = { .probe = avs_pci_probe, .remove = avs_pci_remove, .shutdown = avs_pci_shutdown, + .dev_groups = avs_attr_groups, .driver = { .pm = &avs_dev_pm, }, diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c new file mode 100644 index 000000000000..9d9921e1cd4d --- /dev/null +++ b/sound/soc/intel/avs/icl.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/slab.h> +#include <sound/hdaudio.h> +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "messages.h" + +#define ICL_VS_LTRP_GB_ICCMAX 95 + +#ifdef CONFIG_DEBUG_FS +int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +{ + struct avs_icl_log_state_info *info; + u32 size, num_libs = adev->fw_cfg.max_libs_count; + int i, ret; + + if (fls_long(resource_mask) > num_libs) + return -EINVAL; + size = struct_size(info, logs_priorities_mask, num_libs); + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->aging_timer_period = aging_period; + info->fifo_full_timer_period = fifo_full_period; + info->enable = enable; + if (enable) + for_each_set_bit(i, &resource_mask, num_libs) + info->logs_priorities_mask[i] = *priorities++; + + ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size); + kfree(info); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} +#endif + +union avs_icl_memwnd2_slot_type { + u32 val; + struct { + u32 resource_id:8; + u32 type:24; + }; +} __packed; + +struct avs_icl_memwnd2_desc { + u32 resource_id; + union avs_icl_memwnd2_slot_type slot_id; + u32 vma; +} __packed; + +#define AVS_ICL_MEMWND2_SLOTS_COUNT 15 + +struct avs_icl_memwnd2 { + union { + struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; + u8 rsvd[PAGE_SIZE]; + }; + u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; +} __packed; + +#define AVS_ICL_SLOT_UNUSED \ + ((union avs_icl_memwnd2_slot_type) { 0x00000000U }) +#define AVS_ICL_SLOT_CRITICAL_LOG \ + ((union avs_icl_memwnd2_slot_type) { 0x54524300U }) +#define AVS_ICL_SLOT_DEBUG_LOG \ + ((union avs_icl_memwnd2_slot_type) { 0x474f4c00U }) +#define AVS_ICL_SLOT_GDB_STUB \ + ((union avs_icl_memwnd2_slot_type) { 0x42444700U }) +#define AVS_ICL_SLOT_BROKEN \ + ((union avs_icl_memwnd2_slot_type) { 0x44414544U }) + +static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_type slot_type) +{ + struct avs_icl_memwnd2_desc desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; + int i; + + memcpy_fromio(&desc, avs_sram_addr(adev, AVS_DEBUG_WINDOW), sizeof(desc)); + + for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++) + if (desc[i].slot_id.val == slot_type.val) + return offsetof(struct avs_icl_memwnd2, slot_array) + + avs_skl_log_buffer_offset(adev, i); + return -ENXIO; +} + +int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core) +{ + union avs_icl_memwnd2_slot_type slot_type = AVS_ICL_SLOT_DEBUG_LOG; + int ret; + + slot_type.resource_id = core; + ret = avs_icl_slot_offset(adev, slot_type); + if (ret < 0) + dev_dbg(adev->dev, "No slot offset found for: %x\n", + slot_type.val); + + return ret; +} + +bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +{ + /* Payload-less IPCs do not take part in d0ix toggling. */ + return tx->size; +} + +int avs_icl_set_d0ix(struct avs_dev *adev, bool enable) +{ + int ret; + + ret = avs_ipc_set_d0ix(adev, enable, false); + return AVS_IPC_RET(ret); +} + +int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw) +{ + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_stream *host_stream; + struct snd_pcm_substream substream; + struct snd_dma_buffer dmab; + unsigned int sd_fmt; + u8 ltrp_gb; + int ret; + + /* + * ICCMAX: + * + * For ICL+ platforms, as per HW recommendation LTRP_GB is set to 95us + * during FW load. Its original value shall be restored once load completes. + * + * To avoid DMI/OPIO L1 entry during the load procedure, additional CAPTURE + * stream is allocated and set to run. + */ + + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + + host_stream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); + if (!host_stream) + return -EBUSY; + + ltrp_gb = snd_hdac_chip_readb(bus, VS_LTRP) & AZX_REG_VS_LTRP_GB_MASK; + /* Carries no real data, use default format. */ + sd_fmt = snd_hdac_stream_format(1, 32, 48000); + + ret = snd_hdac_dsp_prepare(hdac_stream(host_stream), sd_fmt, fw->size, &dmab); + if (ret < 0) + goto release_stream; + + snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ICL_VS_LTRP_GB_ICCMAX); + + spin_lock(&bus->reg_lock); + snd_hdac_stream_start(hdac_stream(host_stream)); + spin_unlock(&bus->reg_lock); + + ret = avs_hda_load_basefw(adev, fw); + + spin_lock(&bus->reg_lock); + snd_hdac_stream_stop(hdac_stream(host_stream)); + spin_unlock(&bus->reg_lock); + + snd_hdac_dsp_cleanup(hdac_stream(host_stream), &dmab); + +release_stream: + snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST); + snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ltrp_gb); + + return ret; +} + +const struct avs_dsp_ops avs_icl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_icl_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_icl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_icl_d0ix_toggle, + .set_d0ix = avs_icl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(icl) +}; diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 65bfc83bd1f0..ad0e535b3c2e 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -301,10 +301,10 @@ void avs_dsp_process_response(struct avs_dev *adev, u64 header) complete(&ipc->busy_completion); } -irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) +irqreturn_t avs_irq_handler(struct avs_dev *adev) { - struct avs_dev *adev = dev_id; struct avs_ipc *ipc = adev->ipc; + const struct avs_spec *const spec = adev->spec; u32 adspis, hipc_rsp, hipc_ack; irqreturn_t ret = IRQ_NONE; @@ -312,35 +312,35 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC)) return ret; - hipc_ack = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCIE); - hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); /* DSP acked host's request */ - if (hipc_ack & SKL_ADSP_HIPCIE_DONE) { + if (hipc_ack & spec->hipc->ack_done_mask) { /* * As an extra precaution, mask done interrupt. Code executed * due to complete() found below does not assume any masking. */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_DONE, 0); complete(&ipc->done_completion); /* tell DSP it has our attention */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE, - SKL_ADSP_HIPCIE_DONE, - SKL_ADSP_HIPCIE_DONE); + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, + spec->hipc->ack_done_mask, + spec->hipc->ack_done_mask); /* unmask done interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_DONE, AVS_ADSP_HIPCCTL_DONE); ret = IRQ_HANDLED; } /* DSP sent new response to process */ - if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) { + if (hipc_rsp & spec->hipc->rsp_busy_mask) { /* mask busy interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_BUSY, 0); ret = IRQ_WAKE_THREAD; @@ -349,40 +349,14 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) return ret; } -irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) -{ - struct avs_dev *adev = dev_id; - union avs_reply_msg msg; - u32 hipct, hipcte; - - hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); - hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); - - /* ensure DSP sent new response to process */ - if (!(hipct & SKL_ADSP_HIPCT_BUSY)) - return IRQ_NONE; - - msg.primary = hipct; - msg.ext.val = hipcte; - avs_dsp_process_response(adev, msg.val); - - /* tell DSP we accepted its message */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, - SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); - /* unmask busy interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, - AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); - - return IRQ_HANDLED; -} - static bool avs_ipc_is_busy(struct avs_ipc *ipc) { struct avs_dev *adev = to_avs_dev(ipc->dev); + const struct avs_spec *const spec = adev->spec; u32 hipc_rsp; - hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); - return hipc_rsp & SKL_ADSP_HIPCT_BUSY; + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); + return hipc_rsp & spec->hipc->rsp_busy_mask; } static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout) @@ -440,9 +414,10 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs) { + const struct avs_spec *const spec = adev->spec; u64 reg = ULONG_MAX; - tx->header |= SKL_ADSP_HIPCI_BUSY; + tx->header |= spec->hipc->req_busy_mask; if (read_fwregs) reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); @@ -450,8 +425,8 @@ static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool r if (tx->size) memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); - snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32); - snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX); + snd_hdac_adsp_writel(adev, spec->hipc->req_ext_offset, tx->header >> 32); + snd_hdac_adsp_writel(adev, spec->hipc->req_offset, tx->header & UINT_MAX); } static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, @@ -606,6 +581,7 @@ int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, cons void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) { + const struct avs_spec *const spec = adev->spec; u32 value, mask; /* @@ -617,7 +593,7 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY; value = enable ? mask : 0; - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value); + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, mask, value); } int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index e83ce6a35755..8e34d3536082 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -306,7 +306,7 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) } /* await ROM init */ - ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg, + ret = snd_hdac_adsp_readq_poll(adev, spec->sram->rom_status_offset, reg, (reg & 0xF) == AVS_ROM_INIT_DONE || (reg & 0xF) == APL_ROM_FW_ENTERED, AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 06b4394cabd2..f874e4f0d95f 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -381,6 +381,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) msg.ext.set_d0ix.wake = enable_pg; msg.ext.set_d0ix.streaming = streaming; + msg.ext.set_d0ix.prevent_pg = !enable_pg; request.header = msg.val; diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index d0344e242e5b..4e609a08863c 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -145,8 +145,12 @@ union avs_module_msg { u32 src_queue:3; } bind_unbind; struct { + /* pre-IceLake */ u32 wake:1; u32 streaming:1; + /* IceLake and onwards */ + u32 prevent_pg:1; + u32 prevent_local_cg:1; } set_d0ix; } ext; }; @@ -359,21 +363,45 @@ enum avs_skl_log_priority { AVS_SKL_LOG_VERBOSE, }; -struct skl_log_state { +struct avs_skl_log_state { u32 enable; u32 min_priority; } __packed; -struct skl_log_state_info { +struct avs_skl_log_state_info { u32 core_mask; - struct skl_log_state logs_core[]; + struct avs_skl_log_state logs_core[]; } __packed; -struct apl_log_state_info { +struct avs_apl_log_state_info { u32 aging_timer_period; u32 fifo_full_timer_period; u32 core_mask; - struct skl_log_state logs_core[]; + struct avs_skl_log_state logs_core[]; +} __packed; + +enum avs_icl_log_priority { + AVS_ICL_LOG_CRITICAL = 0, + AVS_ICL_LOG_HIGH, + AVS_ICL_LOG_MEDIUM, + AVS_ICL_LOG_LOW, + AVS_ICL_LOG_VERBOSE, +}; + +enum avs_icl_log_source { + AVS_ICL_LOG_INFRA = 0, + AVS_ICL_LOG_HAL, + AVS_ICL_LOG_MODULE, + AVS_ICL_LOG_AUDIO, + AVS_ICL_LOG_SENSING, + AVS_ICL_LOG_ULP_INFRA, +}; + +struct avs_icl_log_state_info { + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 enable; + u32 logs_priorities_mask[]; } __packed; int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size); diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 3aa16ee8d34c..e785fc2a7008 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -547,6 +547,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod return avs_modext_create(adev, mod); } +static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_soc_component *acomp; + + acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp); + + u32 num_ids = mod->template->num_config_ids; + u32 *ids = mod->template->config_ids; + + for (int i = 0; i < num_ids; i++) { + struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]]; + size_t len = config->length; + void *data = config->data; + u32 param = config->param; + int ret; + + ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id, + param, data, len); + if (ret) { + dev_err(adev->dev, "send initial module config failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + } + + return 0; +} + static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod) { kfree(mod); @@ -580,6 +607,12 @@ avs_path_module_create(struct avs_dev *adev, return ERR_PTR(ret); } + ret = avs_path_module_send_init_configs(adev, mod); + if (ret) { + kfree(mod); + return ERR_PTR(ret); + } + return mod; } diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 4dfc5a1ebb7c..2cafbc392cdb 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -643,6 +643,79 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so return 0; } +static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream) +{ + struct hdac_stream *first_running = NULL; + struct hdac_stream *pos; + struct avs_dev *adev = hdac_to_avs(bus); + + list_for_each_entry(pos, &bus->stream_list, list) { + if (pos->running) { + if (first_running) + break; /* more than one running */ + first_running = pos; + } + } + + /* + * If host_stream is a CAPTURE stream and will be the only one running, + * disable L1SEN to avoid sound clipping. + */ + if (!first_running) { + if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, false); + snd_hdac_stream_start(hdac_stream(host_stream)); + return; + } + + snd_hdac_stream_start(hdac_stream(host_stream)); + /* + * If host_stream is the first stream to break the rule above, + * re-enable L1SEN. + */ + if (list_entry_is_head(pos, &bus->stream_list, list) && + first_running->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, true); +} + +static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream) +{ + struct hdac_stream *first_running = NULL; + struct hdac_stream *pos; + struct avs_dev *adev = hdac_to_avs(bus); + + list_for_each_entry(pos, &bus->stream_list, list) { + if (pos == hdac_stream(host_stream)) + continue; /* ignore stream that is about to be stopped */ + if (pos->running) { + if (first_running) + break; /* more than one running */ + first_running = pos; + } + } + + /* + * If host_stream is a CAPTURE stream and is the only one running, + * re-enable L1SEN. + */ + if (!first_running) { + snd_hdac_stream_stop(hdac_stream(host_stream)); + if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, true); + return; + } + + /* + * If by stopping host_stream there is only a single, CAPTURE stream running + * left, disable L1SEN to avoid sound clipping. + */ + if (list_entry_is_head(pos, &bus->stream_list, list) && + first_running->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, false); + + snd_hdac_stream_stop(hdac_stream(host_stream)); +} + static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -664,7 +737,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_start(hdac_stream(host_stream)); + avs_hda_stream_start(bus, host_stream); spin_unlock_irqrestore(&bus->reg_lock, flags); /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ @@ -694,7 +767,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru dev_err(dai->dev, "pause FE path failed: %d\n", ret); spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_stop(hdac_stream(host_stream)); + avs_hda_stream_stop(bus, host_stream); spin_unlock_irqrestore(&bus->reg_lock, flags); ret = avs_path_reset(data->path); diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 078a0ebafa42..6126adca500c 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -50,6 +50,21 @@ #define SKL_ADSP_HIPCIE_DONE BIT(30) #define SKL_ADSP_HIPCT_BUSY BIT(31) +/* CNL Intel HD Audio Inter-Processor Communication Registers */ +#define CNL_ADSP_IPC_BASE 0xC0 +#define CNL_ADSP_REG_HIPCTDR (CNL_ADSP_IPC_BASE + 0x00) +#define CNL_ADSP_REG_HIPCTDA (CNL_ADSP_IPC_BASE + 0x04) +#define CNL_ADSP_REG_HIPCTDD (CNL_ADSP_IPC_BASE + 0x08) +#define CNL_ADSP_REG_HIPCIDR (CNL_ADSP_IPC_BASE + 0x10) +#define CNL_ADSP_REG_HIPCIDA (CNL_ADSP_IPC_BASE + 0x14) +#define CNL_ADSP_REG_HIPCIDD (CNL_ADSP_IPC_BASE + 0x18) +#define CNL_ADSP_REG_HIPCCTL (CNL_ADSP_IPC_BASE + 0x28) + +#define CNL_ADSP_HIPCTDR_BUSY BIT(31) +#define CNL_ADSP_HIPCTDA_DONE BIT(31) +#define CNL_ADSP_HIPCIDR_BUSY BIT(31) +#define CNL_ADSP_HIPCIDA_DONE BIT(31) + /* Intel HD Audio SRAM windows base addresses */ #define SKL_ADSP_SRAM_BASE_OFFSET 0x8000 #define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000 @@ -57,7 +72,7 @@ #define APL_ADSP_SRAM_WINDOW_SIZE 0x20000 /* Constants used when accessing SRAM, space shared with firmware */ -#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram_base_offset) +#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram->base_offset) #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) #define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) @@ -72,8 +87,8 @@ /* registry I/O helpers */ #define avs_sram_offset(adev, window_idx) \ - ((adev)->spec->sram_base_offset + \ - (adev)->spec->sram_window_size * (window_idx)) + ((adev)->spec->sram->base_offset + \ + (adev)->spec->sram->window_size * (window_idx)) #define avs_sram_addr(adev, window_idx) \ ((adev)->dsp_ba + avs_sram_offset(adev, window_idx)) diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index 6bb8bbc70442..d19f8953993f 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -12,11 +12,36 @@ #include "avs.h" #include "messages.h" +irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) +{ + union avs_reply_msg msg; + u32 hipct, hipcte; + + hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); + + /* Ensure DSP sent new response to process. */ + if (!(hipct & SKL_ADSP_HIPCT_BUSY)) + return IRQ_NONE; + + msg.primary = hipct; + msg.ext.val = hipcte; + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); + /* Unmask busy interrupt. */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_BUSY); + + return IRQ_HANDLED; +} + static int __maybe_unused -skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { - struct skl_log_state_info *info; + struct avs_skl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; int ret, i; @@ -45,7 +70,7 @@ skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri return 0; } -int skl_log_buffer_offset(struct avs_dev *adev, u32 core) +int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core) { return core * avs_log_buffer_size(adev); } @@ -53,8 +78,7 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core) /* fw DbgLogWp registers */ #define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core) -static int -skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { void __iomem *buf; u16 size, write, offset; @@ -74,7 +98,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) return 0; } -static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) { u8 *dump; @@ -88,33 +112,32 @@ static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) return 0; } -static bool -skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { /* unsupported on cAVS 1.5 hw */ return false; } -static int skl_set_d0ix(struct avs_dev *adev, bool enable) +static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable) { /* unsupported on cAVS 1.5 hw */ return 0; } -const struct avs_dsp_ops skl_dsp_ops = { +const struct avs_dsp_ops avs_skl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_dsp_irq_handler, - .irq_thread = avs_dsp_irq_thread, + .irq_handler = avs_irq_handler, + .irq_thread = avs_skl_irq_thread, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, .transfer_mods = avs_cldma_transfer_modules, - .log_buffer_offset = skl_log_buffer_offset, - .log_buffer_status = skl_log_buffer_status, - .coredump = skl_coredump, - .d0ix_toggle = skl_d0ix_toggle, - .set_d0ix = skl_set_d0ix, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_skl_log_buffer_status, + .coredump = avs_skl_coredump, + .d0ix_toggle = avs_skl_d0ix_toggle, + .set_d0ix = avs_skl_set_d0ix, AVS_SET_ENABLE_LOGS_OP(skl) }; diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c new file mode 100644 index 000000000000..cce21636fbc0 --- /dev/null +++ b/sound/soc/intel/avs/sysfs.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/sysfs.h> +#include "avs.h" + +static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct avs_dev *adev = to_avs_dev(dev); + struct avs_fw_version *fw_version = &adev->fw_cfg.fw_version; + + return sysfs_emit(buf, "%d.%d.%d.%d\n", fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build); +} +static DEVICE_ATTR_RO(fw_version); + +static struct attribute *avs_fw_attrs[] = { + &dev_attr_fw_version.attr, + NULL +}; + +static const struct attribute_group avs_attr_group = { + .name = "avs", + .attrs = avs_fw_attrs, +}; + +const struct attribute_group *avs_attr_groups[] = { + &avs_attr_group, + NULL +}; diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c new file mode 100644 index 000000000000..0e052e7f6bac --- /dev/null +++ b/sound/soc/intel/avs/tgl.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include "avs.h" + +static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_power(adev, core_mask, power); +} + +static int avs_tgl_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_reset(adev, core_mask, reset); +} + +static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_stall(adev, core_mask, stall); +} + +const struct avs_dsp_ops avs_tgl_dsp_ops = { + .power = avs_tgl_dsp_core_power, + .reset = avs_tgl_dsp_core_reset, + .stall = avs_tgl_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_icl_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_icl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_icl_d0ix_toggle, + .set_d0ix = avs_icl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(icl) +}; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 48b3c67c9103..13061bd1488b 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1118,6 +1118,21 @@ static const struct avs_tplg_token_parser module_parsers[] = { .offset = offsetof(struct avs_tplg_module, ctl_id), .parse = avs_parse_byte_token, }, + { + .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, num_config_ids), + .parse = avs_parse_byte_token, + }, +}; + +static const struct avs_tplg_token_parser init_config_parsers[] = { + { + .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = 0, + .parse = avs_parse_word_token, + }, }; static struct avs_tplg_module * @@ -1125,17 +1140,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline struct snd_soc_tplg_vendor_array *tuples, u32 block_size) { struct avs_tplg_module *module; + u32 esize; int ret; + /* See where config id block starts. */ + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize); + if (ret) + return ERR_PTR(ret); + module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL); if (!module) return ERR_PTR(-ENOMEM); ret = avs_parse_tokens(comp, module, module_parsers, - ARRAY_SIZE(module_parsers), tuples, block_size); + ARRAY_SIZE(module_parsers), tuples, esize); if (ret < 0) return ERR_PTR(ret); + block_size -= esize; + /* Parse trailing config ids if any. */ + if (block_size) { + u32 num_config_ids = module->num_config_ids; + u32 *config_ids; + + if (!num_config_ids) + return ERR_PTR(-EINVAL); + + config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids), + GFP_KERNEL); + if (!config_ids) + return ERR_PTR(-ENOMEM); + + tuples = avs_tplg_vendor_array_at(tuples, esize); + ret = parse_dictionary_entries(comp, tuples, block_size, + config_ids, num_config_ids, sizeof(*config_ids), + AVS_TKN_MOD_INIT_CONFIG_ID_U32, + init_config_parsers, + ARRAY_SIZE(init_config_parsers)); + if (ret) + return ERR_PTR(ret); + + module->config_ids = config_ids; + } + module->owner = owner; INIT_LIST_HEAD(&module->node); @@ -1416,6 +1464,82 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o return template; } +static const struct avs_tplg_token_parser mod_init_config_parsers[] = { + { + .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_init_config, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_INIT_CONFIG_PARAM_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_init_config, param), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_INIT_CONFIG_LENGTH_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_init_config, length), + .parse = avs_parse_word_token, + }, +}; + +static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + int ret, i; + + /* Parse tuple section telling how many init configs there are. */ + ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs, + &tplg->num_init_configs, + sizeof(*tplg->init_configs), + AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32); + if (ret) + return ret; + + block_size -= le32_to_cpu(tuples->size); + /* With header parsed, move on to parsing entries. */ + tuples = avs_tplg_vendor_array_next(tuples); + + for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) { + struct avs_tplg_init_config *config = &tplg->init_configs[i]; + struct snd_soc_tplg_vendor_array *tmp; + void *init_config_data; + u32 esize; + + /* + * Usually to get section length we search for first token of next group of data, + * but in this case we can't as tuples are followed by raw data. + */ + tmp = avs_tplg_vendor_array_next(tuples); + esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); + + ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), + AVS_TKN_MOD_INIT_CONFIG_ID_U32, + mod_init_config_parsers, + ARRAY_SIZE(mod_init_config_parsers)); + + block_size -= esize; + + /* handle raw data section */ + init_config_data = (void *)tuples + esize; + esize = config->length; + + config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL); + if (!config->data) + return -ENOMEM; + + tuples = init_config_data + esize; + block_size -= esize; + } + + return 0; +} + static int avs_route_load(struct snd_soc_component *comp, int index, struct snd_soc_dapm_route *route) { @@ -1571,6 +1695,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index, struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array; struct avs_soc_component *acomp = to_avs_soc_component(comp); size_t remaining = le32_to_cpu(manifest->priv.size); + bool has_init_config = true; u32 offset; int ret; @@ -1668,8 +1793,43 @@ static int avs_manifest(struct snd_soc_component *comp, int index, remaining -= offset; tuples = avs_tplg_vendor_array_at(tuples, offset); + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset); + if (ret) { + dev_err(comp->dev, "condpath lookup failed: %d\n", ret); + return ret; + } + /* Bindings dictionary. */ - return avs_tplg_parse_bindings(comp, tuples, remaining); + ret = avs_tplg_parse_bindings(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset); + if (ret == -ENOENT) { + dev_dbg(comp->dev, "init config lookup failed: %d\n", ret); + has_init_config = false; + } else if (ret) { + dev_err(comp->dev, "init config lookup failed: %d\n", ret); + return ret; + } + + if (!has_init_config) + return 0; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + /* Initial configs dictionary. */ + ret = avs_tplg_parse_initial_configs(comp, tuples, remaining); + if (ret < 0) + return ret; + + return 0; } #define AVS_CONTROL_OPS_VOLUME 257 diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 6e1c8e9b2496..6a59dd766603 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -33,6 +33,9 @@ struct avs_tplg { u32 num_pplcfgs; struct avs_tplg_binding *bindings; u32 num_bindings; + u32 num_condpath_tmpls; + struct avs_tplg_init_config *init_configs; + u32 num_init_configs; struct list_head path_tmpl_list; }; @@ -147,6 +150,14 @@ struct avs_tplg_path_template { struct list_head node; }; +struct avs_tplg_init_config { + u32 id; + + u8 param; + size_t length; + void *data; +}; + struct avs_tplg_path { u32 id; @@ -183,6 +194,8 @@ struct avs_tplg_module { u8 domain; struct avs_tplg_modcfg_ext *cfg_ext; u32 ctl_id; + u32 num_config_ids; + u32 *config_ids; struct avs_tplg_pipeline *owner; /* Pipeline modules management. */ diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 8fd5e7f83054..18ac3ce0752e 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -677,6 +677,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST depends on SOUNDWIRE + select SND_SOC_INTEL_SOF_BOARD_HELPERS select SND_SOC_MAX98363 select SND_SOC_MAX98373_I2C select SND_SOC_MAX98373_SDW diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c index 4f2cb8e52971..088894ff4165 100644 --- a/sound/soc/intel/boards/sof_board_helpers.c +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -73,6 +73,16 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) /* * DAI Link Helpers */ + +/* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */ +#define DEFAULT_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_CODEC, \ + SOF_LINK_DMIC01, \ + SOF_LINK_DMIC16K, \ + SOF_LINK_IDISP_HDMI, \ + SOF_LINK_AMP, \ + SOF_LINK_BT_OFFLOAD, \ + SOF_LINK_HDMI_IN) + static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -416,6 +426,7 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, int idx = 0; int ret; int ssp_hdmi_in = 0; + unsigned long link_order, link; num_links = calculate_num_links(ctx); @@ -424,94 +435,140 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, if (!links) return -ENOMEM; - /* headphone codec */ - if (ctx->codec_type != CODEC_NONE) { - ret = sof_intel_board_set_codec_link(dev, &links[idx], idx, - ctx->codec_type, - ctx->ssp_codec); - if (ret) { - dev_err(dev, "fail to set codec link, ret %d\n", ret); - return ret; - } - - ctx->codec_link = &links[idx]; - idx++; - } - - /* dmic01 and dmic16k */ - if (ctx->dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, - SOF_DMIC_01); - if (ret) { - dev_err(dev, "fail to set dmic01 link, ret %d\n", ret); - return ret; - } - - idx++; - } - - if (ctx->dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, - SOF_DMIC_16K); - if (ret) { - dev_err(dev, "fail to set dmic16k link, ret %d\n", ret); - return ret; - } - - idx++; - } - - /* idisp HDMI */ - for (i = 1; i <= ctx->hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx, - i, - ctx->hdmi.idisp_codec); - if (ret) { - dev_err(dev, "fail to set hdmi link, ret %d\n", ret); - return ret; + if (ctx->link_order_overwrite) + link_order = ctx->link_order_overwrite; + else + link_order = DEFAULT_LINK_ORDER; + + dev_dbg(dev, "create dai links, link_order 0x%lx\n", link_order); + + while (link_order) { + link = link_order & SOF_LINK_ORDER_MASK; + link_order >>= SOF_LINK_ORDER_SHIFT; + + switch (link) { + case SOF_LINK_CODEC: + /* headphone codec */ + if (ctx->codec_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_codec_link(dev, &links[idx], + idx, + ctx->codec_type, + ctx->ssp_codec); + if (ret) { + dev_err(dev, "fail to set codec link, ret %d\n", + ret); + return ret; + } + + ctx->codec_link = &links[idx]; + idx++; + break; + case SOF_LINK_DMIC01: + /* dmic01 */ + if (ctx->dmic_be_num == 0) + continue; + + /* at least we have dmic01 */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_01); + if (ret) { + dev_err(dev, "fail to set dmic01 link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_DMIC16K: + /* dmic16k */ + if (ctx->dmic_be_num <= 1) + continue; + + /* set up 2 BE links at most */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_16K); + if (ret) { + dev_err(dev, "fail to set dmic16k link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_IDISP_HDMI: + /* idisp HDMI */ + for (i = 1; i <= ctx->hdmi_num; i++) { + ret = sof_intel_board_set_intel_hdmi_link(dev, + &links[idx], + idx, i, + ctx->hdmi.idisp_codec); + if (ret) { + dev_err(dev, "fail to set hdmi link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_AMP: + /* speaker amp */ + if (ctx->amp_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], + idx, + ctx->amp_type, + ctx->ssp_amp); + if (ret) { + dev_err(dev, "fail to set amp link, ret %d\n", + ret); + return ret; + } + + ctx->amp_link = &links[idx]; + idx++; + break; + case SOF_LINK_BT_OFFLOAD: + /* BT audio offload */ + if (!ctx->bt_offload_present) + continue; + + ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, + ctx->ssp_bt); + if (ret) { + dev_err(dev, "fail to set bt link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_HDMI_IN: + /* HDMI-In */ + for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { + ret = sof_intel_board_set_hdmi_in_link(dev, + &links[idx], + idx, + ssp_hdmi_in); + if (ret) { + dev_err(dev, "fail to set hdmi-in link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_NONE: + /* caught here if it's not used as terminator in macro */ + fallthrough; + default: + dev_err(dev, "invalid link type %ld\n", link); + return -EINVAL; } - - idx++; - } - - /* speaker amp */ - if (ctx->amp_type != CODEC_NONE) { - ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx, - ctx->amp_type, - ctx->ssp_amp); - if (ret) { - dev_err(dev, "fail to set amp link, ret %d\n", ret); - return ret; - } - - ctx->amp_link = &links[idx]; - idx++; - } - - /* BT audio offload */ - if (ctx->bt_offload_present) { - ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, - ctx->ssp_bt); - if (ret) { - dev_err(dev, "fail to set bt link, ret %d\n", ret); - return ret; - } - - idx++; - } - - /* HDMI-In */ - for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { - ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx, - ssp_hdmi_in); - if (ret) { - dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret); - return ret; - } - - idx++; } if (idx != num_links) { @@ -527,6 +584,24 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, } EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); +struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, + const char * const dai_name[], int num_dais) +{ + struct snd_soc_dai *dai; + int index; + int i; + + for (index = 0; index < num_dais; index++) + for_each_rtd_codec_dais(rtd, i, dai) + if (strstr(dai->name, dai_name[index])) { + dev_dbg(rtd->card->dev, "get dai %s\n", dai->name); + return dai; + } + + return NULL; +} +EXPORT_SYMBOL_NS(get_codec_dai_by_name, SND_SOC_INTEL_SOF_BOARD_HELPERS); + MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers"); MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index 3b36058118ca..f42d5d640321 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -10,6 +10,29 @@ #include "sof_hdmi_common.h" #include "sof_ssp_common.h" +enum { + SOF_LINK_NONE = 0, + SOF_LINK_CODEC, + SOF_LINK_DMIC01, + SOF_LINK_DMIC16K, + SOF_LINK_IDISP_HDMI, + SOF_LINK_AMP, + SOF_LINK_BT_OFFLOAD, + SOF_LINK_HDMI_IN, +}; + +#define SOF_LINK_ORDER_MASK (0xF) +#define SOF_LINK_ORDER_SHIFT (4) + +#define SOF_LINK_ORDER(k1, k2, k3, k4, k5, k6, k7) \ + ((((k1) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 0)) | \ + (((k2) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 1)) | \ + (((k3) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 2)) | \ + (((k4) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 3)) | \ + (((k5) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 4)) | \ + (((k6) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 5)) | \ + (((k7) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 6))) + /* * sof_rt5682_private: private data for rt5682 machine driver * @@ -37,6 +60,7 @@ struct sof_rt5682_private { * @bt_offload_present: true to create BT offload BE link * @codec_link: pointer to headset codec dai link * @amp_link: pointer to speaker amplifier dai link + * @link_order_overwrite: custom DAI link order * @rt5682: private data for rt5682 machine driver */ struct sof_card_private { @@ -59,6 +83,8 @@ struct sof_card_private { struct snd_soc_dai_link *codec_link; struct snd_soc_dai_link *amp_link; + unsigned long link_order_overwrite; + union { struct sof_rt5682_private rt5682; }; @@ -92,4 +118,7 @@ int sof_intel_board_set_hdmi_in_link(struct device *dev, struct snd_soc_dai_link *link, int be_id, int ssp_hdmi); +struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, + const char * const dai_name[], int num_dais); + #endif /* __SOF_INTEL_BOARD_HELPERS_H */ diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c index c2442bf8ced0..323b86c42ef9 100644 --- a/sound/soc/intel/boards/sof_cs42l42.c +++ b/sound/soc/intel/boards/sof_cs42l42.c @@ -34,25 +34,12 @@ #define SOF_CS42L42_NUM_HDMIDEV_MASK (GENMASK(9, 7)) #define SOF_CS42L42_NUM_HDMIDEV(quirk) \ (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK) -#define SOF_CS42L42_DAILINK_SHIFT 10 -#define SOF_CS42L42_DAILINK_MASK (GENMASK(24, 10)) -#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \ - ((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK) #define SOF_BT_OFFLOAD_PRESENT BIT(25) #define SOF_CS42L42_SSP_BT_SHIFT 26 #define SOF_CS42L42_SSP_BT_MASK (GENMASK(28, 26)) #define SOF_CS42L42_SSP_BT(quirk) \ (((quirk) << SOF_CS42L42_SSP_BT_SHIFT) & SOF_CS42L42_SSP_BT_MASK) -enum { - LINK_NONE = 0, - LINK_HP = 1, - LINK_SPK = 2, - LINK_DMIC = 3, - LINK_HDMI = 4, - LINK_BT = 5, -}; - static struct snd_soc_jack_pin jack_pins[] = { { .pin = "Headphone Jack", @@ -182,156 +169,63 @@ static struct snd_soc_dai_link_component cs42l42_component[] = { } }; -static struct snd_soc_dai_link * -sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, - int ssp_codec, int ssp_amp, int ssp_bt, - int dmic_be_num, int hdmi_num, bool idisp_codec) +static int +sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) { - struct snd_soc_dai_link *links; int ret; - int id = 0; - int link_seq; - int i; - - links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links, - sizeof(struct snd_soc_dai_link), GFP_KERNEL); - if (!links) - goto devm_err; - - link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT; - - while (link_seq) { - int link_type = link_seq & 0x07; - - switch (link_type) { - case LINK_HP: - ret = sof_intel_board_set_codec_link(dev, &links[id], id, - CODEC_CS42L42, - ssp_codec); - if (ret) { - dev_err(dev, "fail to create hp codec dai links, ret %d\n", - ret); - goto devm_err; - } - - /* codec-specific fields */ - links[id].codecs = cs42l42_component; - links[id].num_codecs = ARRAY_SIZE(cs42l42_component); - links[id].init = sof_cs42l42_init; - links[id].exit = sof_cs42l42_exit; - links[id].ops = &sof_cs42l42_ops; - - id++; - break; - case LINK_SPK: - if (amp_type != CODEC_NONE) { - ret = sof_intel_board_set_ssp_amp_link(dev, - &links[id], - id, - amp_type, - ssp_amp); - if (ret) { - dev_err(dev, "fail to create spk amp dai links, ret %d\n", - ret); - goto devm_err; - } - - /* codec-specific fields */ - switch (amp_type) { - case CODEC_MAX98357A: - max_98357a_dai_link(&links[id]); - break; - case CODEC_MAX98360A: - max_98360a_dai_link(&links[id]); - break; - default: - dev_err(dev, "invalid amp type %d\n", - amp_type); - goto devm_err; - } - - id++; - } - break; - case LINK_DMIC: - if (dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, - &links[id], - id, - SOF_DMIC_01); - if (ret) { - dev_err(dev, "fail to create dmic01 link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - - if (dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, - &links[id], - id, - SOF_DMIC_16K); - if (ret) { - dev_err(dev, "fail to create dmic16k link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_HDMI: - for (i = 1; i <= hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, - &links[id], - id, i, - idisp_codec); - if (ret) { - dev_err(dev, "fail to create hdmi link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_BT: - if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { - ret = sof_intel_board_set_bt_link(dev, - &links[id], id, - ssp_bt); - if (ret) { - dev_err(dev, "fail to create bt offload dai links, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_NONE: - /* caught here if it's not used as terminator in macro */ - default: - dev_err(dev, "invalid link type %d\n", link_type); - goto devm_err; - } - - link_seq >>= 3; + + ret = sof_intel_board_set_dai_link(dev, card, ctx); + if (ret) + return ret; + + if (!ctx->codec_link) { + dev_err(dev, "codec link not available"); + return -EINVAL; + } + + /* codec-specific fields for headphone codec */ + ctx->codec_link->codecs = cs42l42_component; + ctx->codec_link->num_codecs = ARRAY_SIZE(cs42l42_component); + ctx->codec_link->init = sof_cs42l42_init; + ctx->codec_link->exit = sof_cs42l42_exit; + ctx->codec_link->ops = &sof_cs42l42_ops; + + if (ctx->amp_type == CODEC_NONE) + return 0; + + if (!ctx->amp_link) { + dev_err(dev, "amp link not available"); + return -EINVAL; } - return links; -devm_err: - return NULL; + /* codec-specific fields for speaker amplifier */ + switch (ctx->amp_type) { + case CODEC_MAX98357A: + max_98357a_dai_link(ctx->amp_link); + break; + case CODEC_MAX98360A: + max_98360a_dai_link(ctx->amp_link); + break; + default: + dev_err(dev, "invalid amp type %d\n", ctx->amp_type); + return -EINVAL; + } + + return 0; } +#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \ + SOF_LINK_CODEC, \ + SOF_LINK_DMIC01, \ + SOF_LINK_IDISP_HDMI, \ + SOF_LINK_NONE, \ + SOF_LINK_NONE, \ + SOF_LINK_NONE) + static int sof_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; - struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; int ret; @@ -348,6 +242,9 @@ static int sof_audio_probe(struct platform_device *pdev) if (soc_intel_is_glk()) { ctx->dmic_be_num = 1; ctx->hdmi_num = 3; + + /* overwrite the DAI link order for GLK boards */ + ctx->link_order_overwrite = GLK_LINK_ORDER; } else { ctx->dmic_be_num = 2; ctx->hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >> @@ -371,25 +268,13 @@ static int sof_audio_probe(struct platform_device *pdev) ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK; - /* compute number of dai links */ - sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num; - - if (ctx->amp_type != CODEC_NONE) - sof_audio_card_cs42l42.num_links++; - if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { + if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) ctx->bt_offload_present = true; - sof_audio_card_cs42l42.num_links++; - } - - dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type, - ctx->ssp_codec, ctx->ssp_amp, - ctx->ssp_bt, ctx->dmic_be_num, - ctx->hdmi_num, - ctx->hdmi.idisp_codec); - if (!dai_links) - return -ENOMEM; - sof_audio_card_cs42l42.dai_link = dai_links; + /* update dai_link */ + ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_cs42l42, ctx); + if (ret) + return ret; sof_audio_card_cs42l42.dev = &pdev->dev; @@ -409,14 +294,12 @@ static const struct platform_device_id board_ids[] = { { .name = "glk_cs4242_mx98357a", .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) | - SOF_CS42L42_SSP_AMP(1)) | - SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE), + SOF_CS42L42_SSP_AMP(1)), }, { .name = "jsl_cs4242_mx98360a", .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) | - SOF_CS42L42_SSP_AMP(1)) | - SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE), + SOF_CS42L42_SSP_AMP(1)), }, { .name = "adl_mx98360a_cs4242", @@ -424,8 +307,7 @@ static const struct platform_device_id board_ids[] = { SOF_CS42L42_SSP_AMP(1) | SOF_CS42L42_NUM_HDMIDEV(4) | SOF_BT_OFFLOAD_PRESENT | - SOF_CS42L42_SSP_BT(2) | - SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)), + SOF_CS42L42_SSP_BT(2)), }, { } }; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index cd50f26d1edb..640d17c6cd35 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -158,46 +158,6 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .callback = sof_rt5682_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_DISCRETE_I2S_BT"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-ALC1019_ALC5682I_I2S"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), }, .driver_data = (void *)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(2) | @@ -810,54 +770,13 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1)), }, { - .name = "jsl_rt5682_rt1015", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "jsl_rt5682_mx98360", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "jsl_rt5682_rt1015p", + .name = "jsl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1)), }, { - .name = "jsl_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0)), - }, - { - .name = "jsl_rt5650", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "tgl_mx98357_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "tgl_rt1011_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "tgl_mx98373_rt5682", + .name = "tgl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | @@ -866,7 +785,7 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "adl_mx98373_rt5682", + .name = "adl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | @@ -882,41 +801,6 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(4)), }, { - .name = "adl_max98390_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_mx98360_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_rt1019_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { .name = "adl_rt5682_c1_h02", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(1) | @@ -925,15 +809,6 @@ static const struct platform_device_id board_ids[] = { SOF_HDMI_CAPTURE_SSP_MASK(0x5)), }, { - .name = "adl_rt5650", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { .name = "rpl_mx98357_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | @@ -941,16 +816,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(4)), }, { - .name = "rpl_mx98360_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "rpl_rt1019_rt5682", + .name = "rpl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | @@ -983,14 +849,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(3)), }, { - .name = "mtl_rt1019_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3)), - }, - { - .name = "mtl_rt5650", + .name = "mtl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(2) | SOF_RT5682_SSP_AMP(0) | diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 300391fbc2fc..08f330ed5c2e 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -236,6 +236,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { SOF_SDW_PCH_DMIC | RT711_JD2_100K), }, + { + /* NUC15 LAPRC710 skews */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + SOF_SDW_PCH_DMIC | + RT711_JD2_100K), + }, /* TigerLake-SDCA devices */ { .callback = sof_sdw_quirk_cb, @@ -650,7 +661,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt700-aif1", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_rt700_init, + .rtd_init = rt700_rtd_init, }, }, .dai_num = 1, @@ -666,6 +677,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, }, .dai_num = 1, @@ -681,6 +693,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, + .rtd_init = rt711_rtd_init, }, }, .dai_num = 1, @@ -696,13 +709,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, { .direction = {true, false}, .dai_name = "rt712-sdca-aif2", .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, - .init = sof_sdw_rt712_spk_init, + .rtd_init = rt712_spk_rtd_init, }, }, .dai_num = 2, @@ -716,7 +730,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt712_sdca_dmic_init, + .rtd_init = rt712_sdca_dmic_rtd_init, }, }, .dai_num = 1, @@ -732,6 +746,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, }, .dai_num = 1, @@ -745,7 +760,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt712_sdca_dmic_init, + .rtd_init = rt712_sdca_dmic_rtd_init, }, }, .dai_num = 1, @@ -761,6 +776,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -776,6 +792,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -790,6 +807,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -804,7 +822,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_sdca_init, + .rtd_init = rt715_sdca_rtd_init, }, }, .dai_num = 1, @@ -819,7 +837,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_sdca_init, + .rtd_init = rt715_sdca_rtd_init, }, }, .dai_num = 1, @@ -834,7 +852,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_init, + .rtd_init = rt715_rtd_init, }, }, .dai_num = 1, @@ -849,7 +867,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_init, + .rtd_init = rt715_rtd_init, }, }, .dai_num = 1, @@ -893,6 +911,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_maxim_init, + .rtd_init = maxim_spk_rtd_init, }, }, .dai_num = 1, @@ -906,6 +925,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_maxim_init, + .rtd_init = maxim_spk_rtd_init, }, }, .dai_num = 1, @@ -918,7 +938,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt5682-sdw", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_rt5682_init, + .rtd_init = rt5682_rtd_init, }, }, .dai_num = 1, @@ -932,6 +952,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_cs_amp_init, + .rtd_init = cs_spk_rtd_init, }, }, .dai_num = 1, @@ -944,7 +965,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "cs42l42-sdw", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_cs42l42_init, + .rtd_init = cs42l42_rtd_init, }, }, .dai_num = 1, @@ -958,14 +979,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "cs42l43-dp5", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, - .init = sof_sdw_cs42l43_hs_init, + .rtd_init = cs42l43_hs_rtd_init, }, { .direction = {false, true}, .dai_name = "cs42l43-dp1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_cs42l43_dmic_init, + .rtd_init = cs42l43_dmic_rtd_init, }, { .direction = {false, true}, @@ -1387,6 +1408,56 @@ static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps, } } +static inline int find_codec_info_dai(const char *dai_name, int *dai_index) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { + for (j = 0; j < codec_info_list[i].dai_num; j++) { + if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) { + *dai_index = j; + return i; + } + } + } + + return -EINVAL; +} + +static int sof_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_sdw_codec_info *codec_info; + struct snd_soc_dai *dai; + int codec_index; + int dai_index; + int ret; + int i; + + for_each_rtd_codec_dais(rtd, i, dai) { + codec_index = find_codec_info_dai(dai->name, &dai_index); + if (codec_index < 0) + return -EINVAL; + + codec_info = &codec_info_list[codec_index]; + /* + * A codec dai can be connected to different dai links for capture and playback, + * but we only need to call the rtd_init function once. + * The rtd_init for each codec dai is independent. So, the order of rtd_init + * doesn't matter. + */ + if (codec_info->dais[dai_index].rtd_init_done) + continue; + if (codec_info->dais[dai_index].rtd_init) { + ret = codec_info->dais[dai_index].rtd_init(rtd); + if (ret) + return ret; + } + codec_info->dais[dai_index].rtd_init_done = true; + } + + return 0; +} + static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, @@ -1547,7 +1618,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, init_dai_link(dev, dai_links + *link_index, be_id, name, playback, capture, cpus, cpu_dai_num, codecs, codec_num, - NULL, &sdw_ops); + sof_sdw_rtd_init, &sdw_ops); /* * SoundWire DAILINKs use 'stream' functions and Bank Switch operations @@ -1696,15 +1767,21 @@ out: return codec_index; for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { + int current_be_id; + ret = create_sdw_dailink(card, &link_index, dai_links, sdw_be_num, adr_link, codec_conf, codec_conf_num, - &be_id, &codec_conf_index, + ¤t_be_id, &codec_conf_index, &ignore_pch_dmic, append_dai_type, i, j); if (ret < 0) { dev_err(dev, "failed to create dai link %d\n", link_index); return ret; } + + /* Update the be_id to match the highest ID used for SDW link */ + if (be_id < current_be_id) + be_id = current_be_id; } if (aggregation && endpoint->aggregated) @@ -1880,6 +1957,7 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { for (j = 0; j < codec_info_list[i].dai_num; j++) { + codec_info_list[i].dais[j].rtd_init_done = false; /* Check each dai in codec_info_lis to see if it is used in the link */ if (!codec_info_list[i].dais[j].exit) continue; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index f16456945edb..b1d57034361c 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -78,6 +78,8 @@ struct sof_sdw_dai_info { struct sof_sdw_codec_info *info, bool playback); int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); + int (*rtd_init)(struct snd_soc_pcm_runtime *rtd); + bool rtd_init_done; /* Indicate that the rtd_init callback is done */ }; struct sof_sdw_codec_info { @@ -137,25 +139,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, bool playback); int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); -/* RT712-SDCA support */ -int sof_sdw_rt712_spk_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); -int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* RT700 support */ -int sof_sdw_rt700_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* RT1308 I2S support */ extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; @@ -167,22 +150,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, bool playback); int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); -/* RT1316 support */ - -/* RT715 support */ -int sof_sdw_rt715_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* RT715-SDCA support */ -int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* RT722-SDCA support */ int sof_sdw_rt722_spk_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, @@ -202,37 +169,28 @@ int sof_sdw_maxim_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); -/* RT5682 support */ -int sof_sdw_rt5682_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* CS42L42 support */ -int sof_sdw_cs42l42_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* CS42L43 support */ -int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* CS AMP support */ int sof_sdw_cs_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); + +/* dai_link init callbacks */ + +int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd); + #endif diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index 436f41086da6..0dc297f7de01 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -15,6 +15,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget cs42l42_widgets[] = { @@ -46,15 +47,24 @@ static struct snd_soc_jack_pin cs42l42_jack_pins[] = { }, }; -static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) +static const char * const jack_codecs[] = { + "cs42l42" +}; + +int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l42", card->components); @@ -111,21 +121,4 @@ static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_cs42l42_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - dai_links->init = cs42l42_rtd_init; - - return 0; -} +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_cs42l43.c b/sound/soc/intel/boards/sof_sdw_cs42l43.c index 360f11b72aa2..a9b6edac2ecd 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l43.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l43.c @@ -50,7 +50,7 @@ static struct snd_soc_jack_pin sof_jack_pins[] = { }, }; -static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) +int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); @@ -108,20 +108,7 @@ static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * No need to test if (!playback) like other codecs as cs42l43 uses separated dai for - * playback and capture, and sof_sdw_cs42l43_init is only linked to the playback dai. - */ - dai_links->init = cs42l43_hs_rtd_init; - - return 0; -} - -static int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -146,11 +133,3 @@ static int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, - bool playback) -{ - dai_links->init = cs42l43_dmic_rtd_init; - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c index f88c01552a92..56cf75bc6cc4 100644 --- a/sound/soc/intel/boards/sof_sdw_cs_amp.c +++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c @@ -18,7 +18,7 @@ static const struct snd_soc_dapm_widget sof_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), }; -static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) +int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { const char *dai_name = rtd->dai_link->codecs->dai_name; struct snd_soc_card *card = rtd->card; @@ -67,7 +67,6 @@ int sof_sdw_cs_amp_init(struct snd_soc_card *card, return 0; info->amp_num++; - dai_links->init = cs_spk_init; return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_maxim.c b/sound/soc/intel/boards/sof_sdw_maxim.c index e36b8d8c70c9..034730432671 100644 --- a/sound/soc/intel/boards/sof_sdw_maxim.c +++ b/sound/soc/intel/boards/sof_sdw_maxim.c @@ -27,7 +27,7 @@ static const struct snd_kcontrol_new maxim_controls[] = { SOC_DAPM_PIN_SWITCH("Right Spk"), }; -static int spk_init(struct snd_soc_pcm_runtime *rtd) +int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -145,8 +145,6 @@ int sof_sdw_maxim_init(struct snd_soc_card *card, bool playback) { info->amp_num++; - if (info->amp_num == 2) - dai_links->init = spk_init; maxim_part_id = info->part_id; switch (maxim_part_id) { diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 7b7c9def398b..6b008a5a343b 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -15,6 +15,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt5682_widgets[] = { @@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt5682_jack_pins[] = { }, }; -static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) +static const char * const jack_codecs[] = { + "rt5682" +}; + +int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt5682", card->components); @@ -110,21 +120,4 @@ static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_rt5682_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - dai_links->init = rt5682_rtd_init; - - return 0; -} +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index a1714afe8515..88e785a54b16 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -13,6 +13,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt700_widgets[] = { @@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt700_jack_pins[] = { }, }; -static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) +static const char * const jack_codecs[] = { + "rt700" +}; + +int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt700", card->components); @@ -109,21 +119,4 @@ static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_rt700_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - dai_links->init = rt700_rtd_init; - - return 0; -} +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index 38782fdfcf15..cdd1587b246c 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -15,6 +15,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" /* @@ -69,15 +70,24 @@ static struct snd_soc_jack_pin rt711_jack_pins[] = { }, }; -static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) +static const char * const jack_codecs[] = { + "rt711" +}; + +int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt711", card->components); @@ -176,7 +186,6 @@ int sof_sdw_rt711_init(struct snd_soc_card *card, } ctx->headset_codec_dev = sdw_dev; - dai_links->init = rt711_rtd_init; - return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c index 3092029419df..ebb4b58c198b 100644 --- a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt712_spk_widgets[] = { @@ -34,7 +35,7 @@ static const struct snd_kcontrol_new rt712_spk_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd) +int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -66,23 +67,21 @@ static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_rt712_spk_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - dai_links->init = rt712_spk_init; - - return 0; -} +static const char * const dmics[] = { + "rt712-sdca-dmic" +}; -static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; + + codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics)); + if (!codec_dai) + return -EINVAL; + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:%s", card->components, component->name_prefix); @@ -91,14 +90,4 @@ static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } - -int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - dai_links->init = rt712_sdca_dmic_rtd_init; - - return 0; -} +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index 7c068dc6b9cf..b5a886cd595d 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -11,7 +11,7 @@ #include <sound/soc-acpi.h> #include "sof_sdw_common.h" -static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -24,13 +24,3 @@ static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } -int sof_sdw_rt715_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - dai_links->init = rt715_rtd_init; - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index ca0cf3db2e4d..4b37a8a6dd2e 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -11,7 +11,7 @@ #include <sound/soc-acpi.h> #include "sof_sdw_common.h" -static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -24,13 +24,3 @@ static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } -int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - dai_links->init = rt715_sdca_rtd_init; - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index 436975b6bdc1..202edab95000 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_amp.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -185,12 +185,14 @@ static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_ return rt1318_map; } -static int first_spk_init(struct snd_soc_pcm_runtime *rtd) +int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; const struct snd_soc_dapm_route *rt_amp_map; char codec_name[CODEC_NAME_SIZE]; + struct snd_soc_dai *dai; int ret; + int i; rt_amp_map = get_codec_name_and_route(rtd, codec_name); @@ -214,40 +216,16 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); - if (ret) - dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); - - return ret; -} - -static int second_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - const struct snd_soc_dapm_route *rt_amp_map; - char codec_name[CODEC_NAME_SIZE]; - int ret; - - rt_amp_map = get_codec_name_and_route(rtd, codec_name); - - ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); - if (ret) - dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); + for_each_rtd_codec_dais(rtd, i, dai) { + if (strstr(dai->component->name_prefix, "-1")) + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); + else if (strstr(dai->component->name_prefix, "-2")) + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); + } return ret; } -static int all_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret; - - ret = first_spk_init(rtd); - if (ret) - return ret; - - return second_spk_init(rtd); -} - static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -317,8 +295,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, return 0; info->amp_num++; - if (info->amp_num == 1) - dai_links->init = first_spk_init; if (info->amp_num == 2) { sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); @@ -342,17 +318,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, return ret; } ctx->amp_dev2 = sdw_dev2; - - /* - * if two amps are in one dai link, the init function - * in this dai link will be first set for the first speaker, - * and it should be reset to initialize all speakers when - * the second speaker is found. - */ - if (dai_links->init) - dai_links->init = all_spk_init; - else - dai_links->init = second_spk_init; } return 0; diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index d9c283829fc7..5253d8332780 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -15,6 +15,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dapm.h> #include <sound/jack.h> +#include "sof_board_helpers.h" #include "sof_sdw_common.h" /* @@ -84,15 +85,24 @@ static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { }, }; -static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) +static const char * const jack_codecs[] = { + "rt711", "rt712", "rt713" +}; + +int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:%s-sdca", card->components, component->name_prefix); @@ -209,7 +219,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, } ctx->headset_codec_dev = sdw_dev; - dai_links->init = rt_sdca_jack_rtd_init; - return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_ssp_common.h b/sound/soc/intel/boards/sof_ssp_common.h index 6d827103479b..d24888bc99fd 100644 --- a/sound/soc/intel/boards/sof_ssp_common.h +++ b/sound/soc/intel/boards/sof_ssp_common.h @@ -67,6 +67,14 @@ enum sof_ssp_codec { enum sof_ssp_codec sof_ssp_detect_codec_type(struct device *dev); enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev); + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SOF_SSP_COMMON) const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type); +#else +static inline const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type) +{ + return NULL; +} +#endif #endif /* __SOF_SSP_COMMON_H */ diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index 346bec000306..5454c6d9ab5b 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -387,7 +387,7 @@ int catpt_dsp_power_down(struct catpt_dev *cdev) mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit; catpt_updatel_pci(cdev, VDRTCTL0, mask, cdev->spec->d3pgd_bit); - catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D3hot); + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot); /* give hw time to drop off */ udelay(50); @@ -411,7 +411,7 @@ int catpt_dsp_power_up(struct catpt_dev *cdev) val = mask & (~CATPT_VDRTCTL2_DTCGE); catpt_updatel_pci(cdev, VDRTCTL2, mask, val); - catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D0); + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0); /* SRAM power gating none */ mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index d3d913458c60..0da79a3ba1f0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -500,7 +500,7 @@ static struct snd_soc_acpi_codecs adl_rt5650_amp = { struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_mx98373_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg", @@ -514,7 +514,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_mx98360_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98360a_amp, .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", @@ -542,7 +542,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_rt1019_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt1019p_amp, .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg", @@ -568,7 +568,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_max98390_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98390_amp, .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", @@ -582,7 +582,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_rt5682", + .drv_name = "adl_rt5682_def", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, { @@ -609,7 +609,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .id = "10EC5650", - .drv_name = "adl_rt5650", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt5650_amp, .sof_tplg_filename = "sof-adl-rt5650.tplg", diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 342bbbb48ca7..a6ac2525df17 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -66,28 +66,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_rt1015", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_rt1015p", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015p_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_mx98360", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682", + .drv_name = "jsl_rt5682_def", .sof_tplg_filename = "sof-jsl-rt5682.tplg", }, { @@ -107,7 +107,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { }, { .id = "10EC5650", - .drv_name = "jsl_rt5650", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt5650_spk, .sof_tplg_filename = "sof-jsl-rt5650.tplg", diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 5897bb6b28b8..74d6dcd7471f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -36,6 +36,46 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +/* + * RT722 is a multi-function codec, three endpoints are created for + * its headset, amp and dmic functions. + */ +static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -45,6 +85,33 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_2_single_adr[] = { + { + .adr = 0x000230025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_endpoints), + .endpoints = rt712_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { + { + .adr = 0x000330025D171201ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt712-dmic" + } +}; + +static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { + { + .adr = 0x000030025d072201ull, + .num_endpoints = ARRAY_SIZE(rt722_endpoints), + .endpoints = rt722_endpoints, + .name_prefix = "rt722" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { { .adr = 0x000230025D131601ull, @@ -81,6 +148,29 @@ static const struct snd_soc_acpi_link_adr lnl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_712_only[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt712_2_single_adr), + .adr_d = rt712_2_single_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1712_3_single_adr), + .adr_d = rt1712_3_single_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr lnl_rt722_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt722_0_single_adr), + .adr_d = rt722_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = { { .mask = BIT(0), @@ -138,6 +228,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711.tplg", }, + { + .link_mask = BIT(2) | BIT(3), + .links = lnl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", + }, + { + .link_mask = BIT(0), + .links = lnl_rt722_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index feb12c6c85d1..e9a5da079089 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -62,7 +62,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { }, { .comp_ids = &mtl_rt5682_rt5682s_hp, - .drv_name = "mtl_rt1019_rt5682", + .drv_name = "mtl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mtl_rt1019p_amp, .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg", @@ -84,7 +84,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { }, { .id = "10EC5650", - .drv_name = "mtl_rt5650", + .drv_name = "mtl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mtl_rt5650_amp, .sof_tplg_filename = "sof-mtl-rt5650.tplg", @@ -377,6 +377,37 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = { + { + .adr = 0x00023201FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP3" + }, + { + .adr = 0x00023301FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP4" + } + +}; + +static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = { + { + .adr = 0x00033001fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00033101fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP2" + } +}; + static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = { /* Expected order: jack -> amp */ { @@ -554,6 +585,26 @@ static const struct snd_soc_acpi_link_adr mtl_cs42l43_cs35l56[] = { {} }; +static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] = { + /* Expected order: jack -> amp */ + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(cs35l56_2_r_adr), + .adr_d = cs35l56_2_r_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs35l56_3_l_adr), + .adr_d = cs35l56_3_l_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { /* mockup tests need to be first */ @@ -600,6 +651,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" }, { + .link_mask = BIT(0) | BIT(2) | BIT(3), + .links = cs42l43_link0_cs35l56_link2_link3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", + }, + { .link_mask = GENMASK(2, 0), .links = mtl_cs42l43_cs35l56, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index c0a643f4725a..00a21af210fa 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -395,7 +395,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { }, { .comp_ids = &rpl_rt5682_hp, - .drv_name = "rpl_mx98360_rt5682", + .drv_name = "rpl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98360a_amp, .sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg", @@ -423,7 +423,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { }, { .comp_ids = &rpl_rt5682_hp, - .drv_name = "rpl_rt1019_rt5682", + .drv_name = "rpl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_rt1019p_amp, .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg", diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index e5f721ba5ed4..0fba0a60d9c7 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -495,21 +495,21 @@ static const struct snd_soc_acpi_codecs tgl_lt6911_hdmi = { struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_mx98357_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_codecs, .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_mx98373_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_max98373_amp, .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg", }, { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_rt1011_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_rt1011_amp, .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg", diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index 7109b81cc3d0..5d1419ed7a62 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -212,11 +212,12 @@ static const char * const aiu_spdif_ids[] = { static int aiu_clk_get(struct device *dev) { struct aiu *aiu = dev_get_drvdata(dev); + struct clk *pclk; int ret; - aiu->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(aiu->pclk)) - return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n"); + pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n"); aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); if (IS_ERR(aiu->spdif_mclk)) @@ -233,18 +234,6 @@ static int aiu_clk_get(struct device *dev) if (ret) return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); - ret = clk_prepare_enable(aiu->pclk); - if (ret) { - dev_err(dev, "peripheral clock enable failed\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - aiu->pclk); - if (ret) - dev_err(dev, "failed to add reset action on pclk"); - return ret; } diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 393b6c2307e4..0f94c8bf6081 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -33,7 +33,6 @@ struct aiu_platform_data { }; struct aiu { - struct clk *pclk; struct clk *spdif_mclk; struct aiu_interface i2s; struct aiu_interface spdif; diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index 65541fdb0038..bebee0ca8e38 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -3,6 +3,7 @@ // Copyright (c) 2018 BayLibre, SAS. // Author: Jerome Brunet <jbrunet@baylibre.com> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -145,8 +146,8 @@ int axg_fifo_pcm_hw_params(struct snd_soc_component *component, /* Enable irq if necessary */ irq_en = runtime->no_period_wakeup ? 0 : FIFO_INT_COUNT_REPEAT; regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), - CTRL0_INT_EN(irq_en)); + CTRL0_INT_EN, + FIELD_PREP(CTRL0_INT_EN, irq_en)); return 0; } @@ -176,9 +177,9 @@ int axg_fifo_pcm_hw_free(struct snd_soc_component *component, { struct axg_fifo *fifo = axg_fifo_data(ss); - /* Disable the block count irq */ + /* Disable irqs */ regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0); + CTRL0_INT_EN, 0); return 0; } @@ -187,13 +188,13 @@ EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free); static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) { regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_INT_CLR(FIFO_INT_MASK), - CTRL1_INT_CLR(mask)); + CTRL1_INT_CLR, + FIELD_PREP(CTRL1_INT_CLR, mask)); /* Clear must also be cleared */ regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_INT_CLR(FIFO_INT_MASK), - 0); + CTRL1_INT_CLR, + FIELD_PREP(CTRL1_INT_CLR, 0)); } static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) @@ -204,7 +205,7 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) regmap_read(fifo->map, FIFO_STATUS1, &status); - status = STATUS1_INT_STS(status) & FIFO_INT_MASK; + status = FIELD_GET(STATUS1_INT_STS, status); if (status & FIFO_INT_COUNT_REPEAT) snd_pcm_period_elapsed(ss); else @@ -254,15 +255,15 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, /* Setup status2 so it reports the memory pointer */ regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_STATUS2_SEL_MASK, - CTRL1_STATUS2_SEL(STATUS2_SEL_DDR_READ)); + CTRL1_STATUS2_SEL, + FIELD_PREP(CTRL1_STATUS2_SEL, STATUS2_SEL_DDR_READ)); /* Make sure the dma is initially disabled */ __dma_enable(fifo, false); /* Disable irqs until params are ready */ regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_MASK), 0); + CTRL0_INT_EN, 0); /* Clear any pending interrupt */ axg_fifo_ack_irq(fifo, FIFO_INT_MASK); diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index df528e8cb7c9..4c48c0a08481 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -21,8 +21,6 @@ struct snd_soc_dai_driver; struct snd_soc_pcm_runtime; #define AXG_FIFO_CH_MAX 128 -#define AXG_FIFO_RATES (SNDRV_PCM_RATE_5512 | \ - SNDRV_PCM_RATE_8000_384000) #define AXG_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ @@ -42,21 +40,19 @@ struct snd_soc_pcm_runtime; #define FIFO_CTRL0 0x00 #define CTRL0_DMA_EN BIT(31) -#define CTRL0_INT_EN(x) ((x) << 16) +#define CTRL0_INT_EN GENMASK(23, 16) #define CTRL0_SEL_MASK GENMASK(2, 0) #define CTRL0_SEL_SHIFT 0 #define FIFO_CTRL1 0x04 -#define CTRL1_INT_CLR(x) ((x) << 0) -#define CTRL1_STATUS2_SEL_MASK GENMASK(11, 8) -#define CTRL1_STATUS2_SEL(x) ((x) << 8) +#define CTRL1_INT_CLR GENMASK(7, 0) +#define CTRL1_STATUS2_SEL GENMASK(11, 8) #define STATUS2_SEL_DDR_READ 0 -#define CTRL1_FRDDR_DEPTH_MASK GENMASK(31, 24) -#define CTRL1_FRDDR_DEPTH(x) ((x) << 24) +#define CTRL1_FRDDR_DEPTH GENMASK(31, 24) #define FIFO_START_ADDR 0x08 #define FIFO_FINISH_ADDR 0x0c #define FIFO_INT_ADDR 0x10 #define FIFO_STATUS1 0x14 -#define STATUS1_INT_STS(x) ((x) << 0) +#define STATUS1_INT_STS GENMASK(7, 0) #define FIFO_STATUS2 0x18 #define FIFO_INIT_ADDR 0x24 #define FIFO_CTRL2 0x28 diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 8c166a5f338c..e97d43ae7fd2 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -7,6 +7,7 @@ * This driver implements the frontend playback DAI of AXG and G12A based SoCs */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/regmap.h> #include <linux/module.h> @@ -59,8 +60,8 @@ static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream, /* Trim the FIFO depth if the period is small to improve latency */ depth = min(period, fifo->depth); val = (depth / AXG_FIFO_BURST) - 1; - regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH_MASK, - CTRL1_FRDDR_DEPTH(val)); + regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH, + FIELD_PREP(CTRL1_FRDDR_DEPTH, val)); return 0; } @@ -109,7 +110,9 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .stream_name = "Playback", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, @@ -184,7 +187,9 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .stream_name = "Playback", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index bc2f2849ecfb..e721f579321e 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -179,9 +179,9 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, SPDIFIN_CTRL1_BASE_TIMER, FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000)); - /* Threshold based on the minimum width between two edges */ + /* Threshold based on the maximum width between two edges */ regmap_update_bits(priv->map, SPDIFIN_CTRL0, - SPDIFIN_CTRL0_WIDTH_SEL, SPDIFIN_CTRL0_WIDTH_SEL); + SPDIFIN_CTRL0_WIDTH_SEL, 0); /* Calculate the last timer which has no threshold */ t_next = axg_spdifin_mode_timer(priv, i, rate); @@ -199,7 +199,7 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, axg_spdifin_write_timer(priv->map, i, t); /* Set the threshold value */ - axg_spdifin_write_threshold(priv->map, i, t + t_next); + axg_spdifin_write_threshold(priv->map, i, 3 * (t + t_next)); /* Save the current timer for the next threshold calculation */ t_next = t; diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 1c3d433cefd2..bf708717635b 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -12,6 +12,9 @@ #include "axg-tdm.h" +/* Maximum bit clock frequency according the datasheets */ +#define MAX_SCLK 100000000 /* Hz */ + enum { TDM_IFACE_PAD, TDM_IFACE_LOOPBACK, @@ -130,7 +133,7 @@ static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_BP_FC: case SND_SOC_DAIFMT_BC_FP: - dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); + dev_err(dai->dev, "only BP_FP and BC_FC are supported\n"); fallthrough; default: return -EINVAL; @@ -153,19 +156,27 @@ static int axg_tdm_iface_startup(struct snd_pcm_substream *substream, return -EINVAL; } - /* Apply component wide rate symmetry */ if (snd_soc_component_active(dai->component)) { + /* Apply component wide rate symmetry */ ret = snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, iface->rate); - if (ret < 0) { - dev_err(dai->dev, - "can't set iface rate constraint\n"); - return ret; - } + + } else { + /* Limit rate according to the slot number and width */ + unsigned int max_rate = + MAX_SCLK / (iface->slots * iface->slot_width); + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, max_rate); } - return 0; + if (ret < 0) + dev_err(dai->dev, "can't set iface rate constraint\n"); + else + ret = 0; + + return ret; } static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, @@ -264,8 +275,8 @@ static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, srate = iface->slots * iface->slot_width * params_rate(params); if (!iface->mclk_rate) { - /* If no specific mclk is requested, default to bit clock * 4 */ - clk_set_rate(iface->mclk, 4 * srate); + /* If no specific mclk is requested, default to bit clock * 2 */ + clk_set_rate(iface->mclk, 2 * srate); } else { /* Check if we can actually get the bit clock from mclk */ if (iface->mclk_rate % srate) { diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index 1a0be177b8fe..e03a6e21c1c6 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -5,6 +5,7 @@ /* This driver implements the frontend capture DAI of AXG based SoCs */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/regmap.h> #include <linux/module.h> @@ -19,12 +20,9 @@ #define CTRL0_TODDR_EXT_SIGNED BIT(29) #define CTRL0_TODDR_PP_MODE BIT(28) #define CTRL0_TODDR_SYNC_CH BIT(27) -#define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) -#define CTRL0_TODDR_TYPE(x) ((x) << 13) -#define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) -#define CTRL0_TODDR_MSB_POS(x) ((x) << 8) -#define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) -#define CTRL0_TODDR_LSB_POS(x) ((x) << 3) +#define CTRL0_TODDR_TYPE GENMASK(15, 13) +#define CTRL0_TODDR_MSB_POS GENMASK(12, 8) +#define CTRL0_TODDR_LSB_POS GENMASK(7, 3) #define CTRL1_TODDR_FORCE_FINISH BIT(25) #define CTRL1_SEL_SHIFT 28 @@ -76,12 +74,12 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, width = params_width(params); regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_TODDR_TYPE_MASK | - CTRL0_TODDR_MSB_POS_MASK | - CTRL0_TODDR_LSB_POS_MASK, - CTRL0_TODDR_TYPE(type) | - CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | - CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); + CTRL0_TODDR_TYPE | + CTRL0_TODDR_MSB_POS | + CTRL0_TODDR_LSB_POS, + FIELD_PREP(CTRL0_TODDR_TYPE, type) | + FIELD_PREP(CTRL0_TODDR_MSB_POS, TODDR_MSB_POS) | + FIELD_PREP(CTRL0_TODDR_LSB_POS, TODDR_MSB_POS - (width - 1))); return 0; } @@ -131,7 +129,9 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .stream_name = "Capture", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, @@ -226,7 +226,9 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .stream_name = "Capture", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 9c6b4dac6893..571f65788c59 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -48,7 +48,6 @@ #define POWER_CFG 0x10 struct t9015 { - struct clk *pclk; struct regulator *avdd; }; @@ -249,6 +248,7 @@ static int t9015_probe(struct platform_device *pdev) struct t9015 *priv; void __iomem *regs; struct regmap *regmap; + struct clk *pclk; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -256,26 +256,14 @@ static int t9015_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, priv); - priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) - return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n"); + pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(dev, PTR_ERR(pclk), "failed to get core clock\n"); priv->avdd = devm_regulator_get(dev, "AVDD"); if (IS_ERR(priv->avdd)) return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); - ret = clk_prepare_enable(priv->pclk); - if (ret) { - dev_err(dev, "core clock enable failed\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - priv->pclk); - if (ret) - return ret; - ret = device_reset(dev); if (ret) { dev_err(dev, "reset failed\n"); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f03c74809324..e05d6ce4c8fa 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -8,9 +8,6 @@ config SND_PXA2XX_SOC the PXA2xx AC97, I2S or SSP interface. You will also need to select the audio interfaces to support below. -config SND_PXA2XX_AC97 - tristate - config SND_PXA2XX_SOC_AC97 tristate "SoC AC97 support for PXA2xx" depends on SND_PXA2XX_SOC diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index e73bd62c033c..80e0ea0ec9fb 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -271,7 +271,6 @@ static void pxa2xx_ac97_dev_remove(struct platform_device *pdev) pxa2xx_ac97_hw_remove(pdev); } -#ifdef CONFIG_PM_SLEEP static int pxa2xx_ac97_dev_suspend(struct device *dev) { return pxa2xx_ac97_hw_suspend(); @@ -282,18 +281,15 @@ static int pxa2xx_ac97_dev_resume(struct device *dev) return pxa2xx_ac97_hw_resume(); } -static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); -#endif static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_dev_probe, .remove_new = pxa2xx_ac97_dev_remove, .driver = { .name = "pxa2xx-ac97", -#ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, -#endif .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), }, }; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 756706d5b493..747041fa7866 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -73,7 +73,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link = card->dai_link; for_each_available_child_of_node(dev->of_node, np) { - dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); + dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL); if (!dlc) { ret = -ENOMEM; goto err_put_np; diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 14cf1a41fb0d..9d103646973a 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1015,7 +1015,7 @@ static int rz_ssi_probe(struct platform_device *pdev) dev_name(&pdev->dev), ssi); if (ret < 0) return dev_err_probe(&pdev->dev, ret, - "irq request error (dma_tx)\n"); + "irq request error (dma_rt)\n"); } else { if (ssi->irq_tx < 0) return ssi->irq_tx; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 516350533e73..1e94edba12eb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -238,8 +238,8 @@ static inline void snd_soc_debugfs_exit(void) { } #endif -static int snd_soc_is_match_dai_args(struct of_phandle_args *args1, - struct of_phandle_args *args2) +static int snd_soc_is_match_dai_args(const struct of_phandle_args *args1, + const struct of_phandle_args *args2) { if (!args1 || !args2) return 0; @@ -283,11 +283,11 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, /* see snd_soc_dai_name_get() */ - if (strcmp(dlc->dai_name, dai->name) == 0) + if (dai->driver->name && + strcmp(dlc->dai_name, dai->driver->name) == 0) return 1; - if (dai->driver->name && - strcmp(dai->driver->name, dlc->dai_name) == 0) + if (strcmp(dlc->dai_name, dai->name) == 0) return 1; if (dai->component->name && @@ -300,12 +300,12 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) { /* see snd_soc_is_matching_dai() */ - if (dai->name) - return dai->name; - if (dai->driver->name) return dai->driver->name; + if (dai->name) + return dai->name; + if (dai->component->name) return dai->component->name; @@ -831,7 +831,8 @@ static struct device_node return of_node; } -struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args) +struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, + const struct of_phandle_args *args) { struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL); @@ -3597,7 +3598,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); -struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args) +struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args) { struct snd_soc_dai *dai; struct snd_soc_component *component; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bffeea80277f..ad8ba8fbbaee 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -725,7 +725,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card = dapm->card; int ret = 0; - trace_snd_soc_bias_level_start(card, level); + trace_snd_soc_bias_level_start(dapm, level); ret = snd_soc_card_set_bias_level(card, dapm, level); if (ret != 0) @@ -739,7 +739,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, ret = snd_soc_card_set_bias_level_post(card, dapm, level); out: - trace_snd_soc_bias_level_done(card, level); + trace_snd_soc_bias_level_done(dapm, level); return ret; } @@ -1963,7 +1963,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) snd_soc_dapm_mutex_assert_held(card); - trace_snd_soc_dapm_start(card); + trace_snd_soc_dapm_start(card, event); for_each_card_dapms(card, d) { if (dapm_idle_bias_off(d)) @@ -2088,7 +2088,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) "DAPM sequencing finished, waiting %dms\n", card->pop_time); pop_wait(card->pop_time); - trace_snd_soc_dapm_done(card); + trace_snd_soc_dapm_done(card, event); return 0; } diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 19c5e27a193f..2729c6eb3feb 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" + depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD depends on X86 || COMPILE_TEST help This adds support for Sound Open Firmware for AMD platforms. @@ -60,10 +61,27 @@ config SND_SOC_SOF_ACP_PROBES This option is not user-selectable but automatically handled by 'select' statements at a higher level +config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + tristate + select SND_AMD_SOUNDWIRE_ACPI if ACPI + +config SND_SOC_SOF_AMD_SOUNDWIRE + tristate "SOF support for SoundWire based AMD platforms" + default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + depends on ACPI + depends on SOUNDWIRE_AMD + help + This adds support for SoundWire with Sound Open Firmware + for AMD platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + config SND_SOC_SOF_AMD_ACP63 tristate "SOF support for ACP6.3 platform" depends on SND_SOC_SOF_PCI select SND_SOC_SOF_AMD_COMMON + select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE help Select this option for SOF support on AMD ACP6.3 version based platforms. diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index 2d72c6d55dc8..0fc4e20ec673 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -118,16 +118,72 @@ void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags) &panic_info, stack, AMD_STACK_DUMP_SIZE); } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE) +static int amd_sof_sdw_get_slave_info(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data = sdev->pdata->hw_pdata; + + return sdw_amd_get_slave_info(acp_data->sdw); +} + +static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_soc_acpi_mach *mach; + const struct snd_soc_acpi_link_adr *link; + struct acp_dev_data *acp_data = sdev->pdata->hw_pdata; + int ret, i; + + if (acp_data->info.count) { + ret = amd_sof_sdw_get_slave_info(sdev); + if (ret) { + dev_info(sdev->dev, "failed to read slave information\n"); + return NULL; + } + for (mach = sdev->pdata->desc->alt_machines; mach; mach++) { + if (!mach->links) + break; + link = mach->links; + for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { + if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, + acp_data->sdw->ids, + acp_data->sdw->num_slaves)) + break; + } + if (i == acp_data->info.count || !link->num_adr) + break; + } + if (mach && mach->link_mask) { + mach->mach_params.links = mach->links; + mach->mach_params.link_mask = mach->link_mask; + mach->mach_params.platform = dev_name(sdev->dev); + return mach; + } + } + dev_info(sdev->dev, "No SoundWire machine driver found\n"); + return NULL; +} + +#else +static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev) +{ + return NULL; +} +#endif + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach *mach = NULL; - mach = snd_soc_acpi_find_machine(desc->machines); + if (desc->machines) + mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { - dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); - return NULL; + mach = amd_sof_sdw_machine_select(sdev); + if (!mach) { + dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); + return NULL; + } } sof_pdata->tplg_filename = mach->sof_tplg_filename; @@ -204,5 +260,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON); MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); MODULE_DESCRIPTION("ACP SOF COMMON Driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index a913f1cc4c80..59afbe2e0f42 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -65,7 +65,10 @@ /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 #define ACP5X_EXT_INTR_STAT 0x1808 +#define ACP6X_EXTERNAL_INTR_ENB 0x1A00 +#define ACP6X_EXTERNAL_INTR_CNTL 0x1A04 #define ACP6X_EXT_INTR_STAT 0x1A0C +#define ACP6X_EXT_INTR_STAT1 0x1A10 #define ACP3X_DSP_SW_INTR_BASE 0x1814 #define ACP5X_DSP_SW_INTR_BASE 0x1814 @@ -78,6 +81,10 @@ #define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 +/* ACP common registers to report errors related to I2S & SoundWire interfaces */ +#define ACP_SW0_I2S_ERROR_REASON 0x18B4 +#define ACP_SW1_I2S_ERROR_REASON 0x1A50 + /* Registers from ACP_SHA block */ #define ACP_SHA_DSP_FW_QUALIFIER 0x1C70 #define ACP_SHA_DMA_CMD 0x1CB0 @@ -96,4 +103,7 @@ /* Cache window registers */ #define ACP_DSP0_CACHE_OFFSET0 0x0420 #define ACP_DSP0_CACHE_SIZE0 0x0424 + +#define ACP_SW0_EN 0x3000 +#define ACP_SW1_EN 0x3C00 #endif diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index e05eb7a86dd4..d2d21478399e 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -267,29 +267,49 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata = plat_data->hw_pdata; + const char *fw_filename; int ret; - ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev); + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_code_bin); + if (!fw_filename) + return -ENOMEM; + + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware code bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0, - (void *)sdev->basefw.fw->data, sdev->basefw.fw->size); + (void *)sdev->basefw.fw->data, + sdev->basefw.fw->size); + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_data_bin); + if (!fw_filename) + return -ENOMEM; - ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev); + ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware data bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, - (void *)adata->fw_dbin->data, adata->fw_dbin->size); + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); return ret; } EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 07632ae6ccf5..9b3c26210db3 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -28,11 +28,10 @@ MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); const struct dmi_system_id acp_sof_quirk_table[] = { { - /* Valve Jupiter device */ + /* Steam Deck OLED device */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Valve"), DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), }, .driver_data = (void *)SECURED_FIRMWARE, }, @@ -374,10 +373,13 @@ static irqreturn_t acp_irq_thread(int irq, void *context) static irqreturn_t acp_irq_handler(int irq, void *dev_id) { + struct amd_sdw_manager *amd_manager; struct snd_sof_dev *sdev = dev_id; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + struct acp_dev_data *adata = sdev->pdata->hw_pdata; unsigned int base = desc->dsp_intr_base; unsigned int val; + int irq_flag = 0; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); if (val & ACP_DSP_TO_HOST_IRQ) { @@ -386,7 +388,38 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) return IRQ_WAKE_THREAD; } - return IRQ_NONE; + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); + if (val & ACP_SDW0_IRQ_MASK) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SDW0_IRQ_MASK); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + + if (val & ACP_ERROR_IRQ_MASK) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW0_I2S_ERROR_REASON, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW1_I2S_ERROR_REASON, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_ERROR_STATUS, 0); + irq_flag = 1; + } + + if (desc->ext_intr_stat1) { + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1); + if (val & ACP_SDW1_IRQ_MASK) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, + ACP_SDW1_IRQ_MASK); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + } + if (irq_flag) + return IRQ_HANDLED; + else + return IRQ_NONE; } static int acp_power_on(struct snd_sof_dev *sdev) @@ -442,6 +475,33 @@ static int acp_reset(struct snd_sof_dev *sdev) if (desc->ext_intr_enb) snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01); + if (desc->ext_intr_cntl) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK); + return ret; +} + +static int acp_dsp_reset(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_ASSERT_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, + val & ACP_DSP_SOFT_RESET_DONE_MASK, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "timeout asserting reset\n"); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_RELEASE_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in releasing reset\n"); + return ret; } @@ -461,10 +521,34 @@ static int acp_init(struct snd_sof_dev *sdev) return acp_reset(sdev); } +static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + u32 sdw0_en, sdw1_en; + + acp_data = sdev->pdata->hw_pdata; + if (!acp_data->sdw) + return false; + + sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN); + sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN); + acp_data->sdw_en_stat = sdw0_en || sdw1_en; + return acp_data->sdw_en_stat; +} + int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) { int ret; + /* When acp_reset() function is invoked, it will apply ACP SOFT reset and + * DSP reset. ACP Soft reset sequence will cause all ACP IP registers will + * be reset to default values which will break the ClockStop Mode functionality. + * Add a condition check to apply DSP reset when SoundWire ClockStop mode + * is selected. For the rest of the scenarios, apply acp reset sequence. + */ + if (check_acp_sdw_enable_status(sdev)) + return acp_dsp_reset(sdev); + ret = acp_reset(sdev); if (ret) { dev_err(sdev->dev, "ACP Reset failed\n"); @@ -480,20 +564,100 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_resume(struct snd_sof_dev *sdev) { int ret; + struct acp_dev_data *acp_data; - ret = acp_init(sdev); - if (ret) { - dev_err(sdev->dev, "ACP Init failed\n"); - return ret; + acp_data = sdev->pdata->hw_pdata; + if (!acp_data->sdw_en_stat) { + ret = acp_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Init failed\n"); + return ret; + } + return acp_memory_init(sdev); + } else { + return acp_dsp_reset(sdev); } - return acp_memory_init(sdev); } EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE) +static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr) +{ + struct acpi_device *sdw_dev; + struct acp_dev_data *acp_data; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + + if (!addr) + return -ENODEV; + + acp_data = sdev->pdata->hw_pdata; + sdw_dev = acpi_find_child_device(ACPI_COMPANION(sdev->dev), addr, 0); + if (!sdw_dev) + return -ENODEV; + + acp_data->info.handle = sdw_dev->handle; + acp_data->info.count = desc->sdw_max_link_count; + + return amd_sdw_scan_controller(&acp_data->info); +} + +static int amd_sof_sdw_probe(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + struct sdw_amd_res sdw_res; + int ret; + + acp_data = sdev->pdata->hw_pdata; + + memset(&sdw_res, 0, sizeof(sdw_res)); + sdw_res.addr = acp_data->addr; + sdw_res.reg_range = acp_data->reg_range; + sdw_res.handle = acp_data->info.handle; + sdw_res.parent = sdev->dev; + sdw_res.dev = sdev->dev; + sdw_res.acp_lock = &acp_data->acp_lock; + sdw_res.count = acp_data->info.count; + sdw_res.link_mask = acp_data->info.link_mask; + sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR]; + + ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); + if (ret) + dev_err(sdev->dev, "SoundWire probe failed\n"); + return ret; +} + +static int amd_sof_sdw_exit(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + + acp_data = sdev->pdata->hw_pdata; + if (acp_data->sdw) + sdw_amd_exit(acp_data->sdw); + acp_data->sdw = NULL; + + return 0; +} + +#else +static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr) +{ + return 0; +} + +static int amd_sof_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static int amd_sof_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); - struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; const struct sof_amd_acp_desc *chip; const struct dmi_system_id *dmi_id; @@ -526,7 +690,9 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) } pci_set_master(pci); - + adata->addr = addr; + adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; + mutex_init(&adata->acp_lock); sdev->pdata->hw_pdata = adata; adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL); if (!adata->smn_dev) { @@ -548,6 +714,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) if (ret < 0) goto free_ipc_irq; + /* scan SoundWire capabilities exposed by DSDT */ + ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr); + if (ret < 0) { + dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n"); + goto skip_soundwire; + } + ret = amd_sof_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + free_irq(sdev->ipc_irq, sdev); + pci_dev_put(adata->smn_dev); + return ret; + } + +skip_soundwire: sdev->dsp_box.offset = 0; sdev->dsp_box.size = BOX_SIZE_512; @@ -560,17 +741,25 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) adata->signed_fw_image = false; dmi_id = dmi_first_match(acp_sof_quirk_table); if (dmi_id && dmi_id->driver_data) { - adata->fw_code_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-code.bin", - plat_data->fw_filename_prefix, - chip->name); - adata->fw_data_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-data.bin", - plat_data->fw_filename_prefix, - chip->name); - adata->signed_fw_image = dmi_id->driver_data; + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-code.bin", + chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } - dev_dbg(sdev->dev, "fw_code_bin:%s, fw_data_bin:%s\n", adata->fw_code_bin, - adata->fw_data_bin); + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-data.bin", + chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + + adata->signed_fw_image = dmi_id->driver_data; } + adata->enable_fw_debug = enable_fw_debug; acp_memory_init(sdev); @@ -595,6 +784,9 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) if (adata->smn_dev) pci_dev_put(adata->smn_dev); + if (adata->sdw) + amd_sof_sdw_exit(sdev); + if (sdev->ipc_irq) free_irq(sdev->ipc_irq, sdev); @@ -606,4 +798,6 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON); MODULE_DESCRIPTION("AMD ACP sof driver"); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); +MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index c645aee216fd..947068da39b5 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -12,7 +12,7 @@ #define __SOF_AMD_ACP_H #include <linux/dmi.h> - +#include <linux/soundwire/sdw_amd.h> #include "../sof-priv.h" #include "../sof-audio.h" @@ -31,6 +31,9 @@ #define ACP_ASSERT_RESET 0x01 #define ACP_RELEASE_RESET 0x00 #define ACP_SOFT_RESET_DONE_MASK 0x00010001 +#define ACP_DSP_ASSERT_RESET 0x04 +#define ACP_DSP_RELEASE_RESET 0x00 +#define ACP_DSP_SOFT_RESET_DONE_MASK 0x00050004 #define ACP_DSP_INTR_EN_MASK 0x00000001 #define ACP3X_SRAM_PTE_OFFSET 0x02050000 @@ -93,8 +96,13 @@ #define PROBE_STATUS_BIT BIT(31) #define ACP_FIRMWARE_SIGNATURE 0x100 +#define ACP_ERROR_IRQ_MASK BIT(29) +#define ACP_SDW0_IRQ_MASK BIT(21) +#define ACP_SDW1_IRQ_MASK BIT(2) +#define SDW_ACPI_ADDR_ACP63 5 #define ACP_DEFAULT_SRAM_LENGTH 0x00080000 #define ACP_SRAM_PAGE_COUNT 128 +#define ACP6X_SDW_MAX_MANAGER_COUNT 2 enum clock_source { ACP_CLOCK_96M = 0, @@ -184,13 +192,19 @@ struct sof_amd_acp_desc { unsigned int host_bridge_id; u32 pgfsm_base; u32 ext_intr_enb; + u32 ext_intr_cntl; u32 ext_intr_stat; + u32 ext_intr_stat1; u32 dsp_intr_base; u32 sram_pte_offset; u32 hw_semaphore_offset; u32 acp_clkmux_sel; u32 fusion_dsp_offset; u32 probe_reg_offset; + u32 reg_start_addr; + u32 reg_end_addr; + u32 sdw_max_link_count; + u64 sdw_acpi_dev_addr; }; /* Common device data struct for ACP devices */ @@ -199,6 +213,12 @@ struct acp_dev_data { const struct firmware *fw_dbin; /* DMIC device */ struct platform_device *dmic_dev; + /* mutex lock to protect ACP common registers access */ + struct mutex acp_lock; + /* ACPI information stored between scan and probe steps */ + struct sdw_amd_acpi_info info; + /* sdw context allocated by SoundWire driver */ + struct sdw_amd_ctx *sdw; unsigned int fw_bin_size; unsigned int fw_data_bin_size; unsigned int fw_sram_data_bin_size; @@ -207,6 +227,9 @@ struct acp_dev_data { const char *fw_sram_data_bin; u32 fw_bin_page_count; u32 fw_data_bin_page_count; + u32 addr; + u32 reg_range; + u32 blk_type; dma_addr_t sha_dma_addr; u8 *bin_buf; dma_addr_t dma_addr; @@ -222,6 +245,7 @@ struct acp_dev_data { bool enable_fw_debug; bool is_dram_in_use; bool is_sram_in_use; + bool sdw_en_stat; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); diff --git a/sound/soc/sof/amd/pci-acp63.c b/sound/soc/sof/amd/pci-acp63.c index bceb94ac80a9..eeaa12cceb23 100644 --- a/sound/soc/sof/amd/pci-acp63.c +++ b/sound/soc/sof/amd/pci-acp63.c @@ -31,12 +31,19 @@ static const struct sof_amd_acp_desc acp63_chip_info = { .rev = 6, .host_bridge_id = HOST_BRIDGE_ACP63, .pgfsm_base = ACP6X_PGFSM_BASE, + .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, + .ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL, .ext_intr_stat = ACP6X_EXT_INTR_STAT, + .ext_intr_stat1 = ACP6X_EXT_INTR_STAT1, .dsp_intr_base = ACP6X_DSP_SW_INTR_BASE, .sram_pte_offset = ACP6X_SRAM_PTE_OFFSET, .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0, .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL, .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0, + .sdw_max_link_count = ACP6X_SDW_MAX_MANAGER_COUNT, + .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP63, + .reg_start_addr = ACP6x_REG_START, + .reg_end_addr = ACP6x_REG_END, }; static const struct sof_dev_desc acp63_desc = { diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 425b023b03b4..9b00ede2a486 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -679,6 +679,16 @@ int snd_sof_device_remove(struct device *dev) */ snd_sof_machine_unregister(sdev, pdata); + /* + * Balance the runtime pm usage count in case we are faced with an + * exception and we forcably prevented D3 power state to preserve + * context + */ + if (sdev->d3_prevented) { + sdev->d3_prevented = false; + pm_runtime_put_noidle(sdev->dev); + } + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { sof_fw_trace_free(sdev); ret = snd_sof_dsp_power_down_notify(sdev); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d547318e0d32..7c8aafca8fde 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -433,13 +433,15 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg) { - if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || - sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { + if ((IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || + sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) && !sdev->d3_prevented) { /* should we prevent DSP entering D3 ? */ if (!sdev->ipc_dump_printed) dev_info(sdev->dev, "Attempting to prevent DSP from entering D3 state to preserve context\n"); - pm_runtime_get_if_in_use(sdev->dev); + + if (pm_runtime_get_if_in_use(sdev->dev) == 1) + sdev->d3_prevented = true; } /* dump vital information to the logs */ diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c index 138a1ca2c4a8..b56b14232444 100644 --- a/sound/soc/sof/fw-file-profile.c +++ b/sound/soc/sof/fw-file-profile.c @@ -89,6 +89,12 @@ static int sof_test_topology_file(struct device *dev, return ret; } +static bool sof_platform_uses_generic_loader(struct snd_sof_dev *sdev) +{ + return (sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_raw || + sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_memcpy); +} + static int sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, enum sof_ipc_type ipc_type, @@ -130,7 +136,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, * For default path and firmware name do a verification before * continuing further. */ - if (base_profile->fw_path || base_profile->fw_name) { + if ((base_profile->fw_path || base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) { ret = sof_test_firmware_file(dev, out_profile, &ipc_type); if (ret) return ret; @@ -181,7 +188,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, out_profile->ipc_type = ipc_type; /* Test only default firmware file */ - if (!base_profile->fw_path && !base_profile->fw_name) + if ((!base_profile->fw_path && !base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) ret = sof_test_firmware_file(dev, out_profile, NULL); if (!ret) @@ -267,7 +275,11 @@ static void sof_print_profile_info(struct snd_sof_dev *sdev, dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type); - dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name); + /* The firmware path is only valid when generic loader is used */ + if (sof_platform_uses_generic_loader(sdev)) + dev_info(dev, " Firmware file: %s/%s\n", + profile->fw_path, profile->fw_name); + if (profile->fw_lib_path) dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name); diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index d777e70250ef..07f51489d6c9 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -607,7 +607,22 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; +static struct snd_sof_of_mach sof_imx8_machs[] = { + { + .compatible = "fsl,imx8qxp-mek", + .sof_tplg_filename = "sof-imx8-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + { + .compatible = "fsl,imx8qm-mek", + .sof_tplg_filename = "sof-imx8-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8qxp_desc = { + .of_machines = sof_imx8_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { @@ -624,6 +639,7 @@ static struct sof_dev_desc sof_of_imx8qxp_desc = { }; static struct sof_dev_desc sof_of_imx8qm_desc = { + .of_machines = sof_imx8_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 1b976fa500aa..222cd1467da6 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; +static struct snd_sof_of_mach sof_imx8mp_machs[] = { + { + .compatible = "fsl,imx8mp-evk", + .sof_tplg_filename = "sof-imx8mp-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8mp_desc = { + .of_machines = sof_imx8mp_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c index 2badca75782b..7b527ffde488 100644 --- a/sound/soc/sof/imx/imx8ulp.c +++ b/sound/soc/sof/imx/imx8ulp.c @@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = { .set_power_state = imx8ulp_dsp_set_power_state, }; +static struct snd_sof_of_mach sof_imx8ulp_machs[] = { + { + .compatible = "fsl,imx8ulp-evk", + .sof_tplg_filename = "sof-imx8ulp-btsco.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8ulp_desc = { + .of_machines = sof_imx8ulp_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 26105d8f1bdc..2b385cddc385 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -83,6 +83,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, + .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ .suspend = hda_dsp_suspend, diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 55ce75db23e5..c50ca9e72d37 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -522,6 +522,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = { + .get_hext_stream = hda_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .trigger = hda_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -596,6 +607,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = { + .get_hext_stream = hda_dspless_get_hext_stream, + .setup_hext_stream = hda_dspless_setup_hext_stream, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + #endif const struct hda_dai_widget_dma_ops * @@ -603,12 +621,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct snd_sof_dai *sdai; + const struct sof_intel_dsp_desc *chip; - if (sdev->dspless_mode_selected) - return &hda_dspless_dma_ops; - + chip = get_chip_info(sdev->pdata); sdai = swidget->private; + if (sdev->dspless_mode_selected) { + switch (sdai->type) { + case SOF_DAI_INTEL_HDA: + return &hda_dspless_dma_ops; + case SOF_DAI_INTEL_ALH: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &sdw_dspless_dma_ops; + default: + return NULL; + } + } + switch (sdev->pdata->ipc_type) { case SOF_IPC_TYPE_3: { @@ -620,22 +650,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg } case SOF_IPC_TYPE_4: { - struct sof_ipc4_copier *ipc4_copier = sdai->private; - const struct sof_intel_dsp_desc *chip; - - chip = get_chip_info(sdev->pdata); + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - switch (ipc4_copier->dai_type) { + switch (sdai->type) { case SOF_DAI_INTEL_HDA: - { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->use_chain_dma) return &hda_ipc4_chain_dma_ops; return &hda_ipc4_dma_ops; - } case SOF_DAI_INTEL_SSP: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; @@ -647,6 +670,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg case SOF_DAI_INTEL_ALH: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; + if (pipeline->use_chain_dma) + return &sdw_ipc4_chain_dma_ops; return &sdw_ipc4_dma_ops; default: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index f4cbc0ad5de3..c1682bcdb5a6 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -83,12 +83,13 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai sdev = widget_to_sdev(w); - /* - * The swidget parameter of hda_select_dai_widget_ops() is ignored in - * case of DSPless mode - */ + if (!swidget) { + dev_err(sdev->dev, "%s: swidget is NULL\n", __func__); + return NULL; + } + if (sdev->dspless_mode_selected) - return hda_select_dai_widget_ops(sdev, NULL); + return hda_select_dai_widget_ops(sdev, swidget); sdai = swidget->private; @@ -368,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, return ret; } - /* get stream_id */ sdev = widget_to_sdev(w); + if (sdev->dspless_mode_selected) + goto skip_tlv; + + /* get stream_id */ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) { @@ -402,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ dma_config->dma_priv_config_size = 0; +skip_tlv: return 0; } diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 2445ae7f6b2e..31ffa1a8f2ac 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -748,6 +748,7 @@ skip_dsp: static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) { + const struct sof_intel_dsp_desc *chip; int ret; /* display codec must be powered before link reset */ @@ -780,6 +781,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) hda_dsp_ctrl_ppcap_int_enable(sdev, true); } + chip = get_chip_info(sdev->pdata); + if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) + hda_sdw_int_enable(sdev, true); + cleanup: /* display codec can powered off after controller init */ hda_codec_i915_display_power(sdev, false); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index f2ebadbbcc10..b387b1a69d7e 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -21,6 +21,7 @@ #include <trace/events/sof_intel.h> #include "../ops.h" #include "../sof-audio.h" +#include "../ipc4-priv.h" #include "hda.h" #define HDA_LTRP_GB_VALUE_US 95 @@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* store total stream count (playback + capture) from GCAP */ sof_hda->stream_max = num_total; + /* store stream count from GCAP required for CHAIN_DMA */ + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + + ipc4_data->num_playback_streams = num_playback; + ipc4_data->num_capture_streams = num_capture; + } + return 0; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index fe4ae349dad5..7fe72b065451 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -46,44 +46,83 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask) { const struct sof_intel_dsp_desc *chip; - u32 interface_mask[2] = { 0 }; chip = get_chip_info(sdev->pdata); switch (chip->hw_ip_version) { case SOF_INTEL_TANGIER: case SOF_INTEL_BAYTRAIL: case SOF_INTEL_BROADWELL: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); + interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP); break; case SOF_INTEL_CAVS_1_5: case SOF_INTEL_CAVS_1_5_PLUS: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_CAVS_1_8: case SOF_INTEL_CAVS_2_0: case SOF_INTEL_CAVS_2_5: case SOF_INTEL_ACE_1_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_ACE_2_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_HOST_ACCESS] = + interface_mask[SOF_DAI_DSP_ACCESS]; break; default: break; } +} + +static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + + hda_get_interfaces(sdev, interface_mask); return interface_mask[sdev->dspless_mode_selected]; } +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + const struct sof_intel_dsp_desc *chip; + + if (sdev->dspless_mode_selected) + return false; + + hda_get_interfaces(sdev, interface_mask); + + if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type))) + return false; + + if (dai_type == SOF_DAI_INTEL_HDA) + return true; + + switch (dai_type) { + case SOF_DAI_INTEL_SSP: + case SOF_DAI_INTEL_DMIC: + case SOF_DAI_INTEL_ALH: + chip = get_chip_info(sdev->pdata); + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return false; + return true; + default: + return false; + } +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* @@ -1192,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip; int ret = 0; hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", @@ -1305,12 +1345,28 @@ skip_dsp_setup: INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work); } + chip = get_chip_info(sdev->pdata); + if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "could not startup SoundWire links\n"); + goto disable_pp_cap; + } + + hda_sdw_int_enable(sdev, true); + } + init_waitqueue_head(&hdev->waitq); hdev->nhlt = intel_nhlt_init(sdev->dev); return 0; +disable_pp_cap: + if (!sdev->dspless_mode_selected) { + hda_dsp_ctrl_ppcap_int_enable(sdev, false); + hda_dsp_ctrl_ppcap_enable(sdev, false); + } free_ipc_irq: free_irq(sdev->ipc_irq, sdev); free_irq_vector: diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1592e27bc14d..b36eb7c78913 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -574,6 +574,11 @@ struct sof_intel_hda_stream { #define SOF_STREAM_SD_OFFSET_CRST 0x1 /* + * DAI support + */ +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type); + +/* * DSP Core services. */ int hda_dsp_probe_early(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index 30712ea05a7a..7ae017a00184 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -77,6 +77,19 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); } +static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev) +{ + if (sdev->first_boot) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + + /* Check if IMR boot is usable */ + if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) + hda->imrboot_supported = true; + } + + return 0; +} + int sof_lnl_ops_init(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data; @@ -85,7 +98,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); /* probe */ - sof_lnl_ops.probe = lnl_hda_dsp_probe; + if (!sdev->dspless_mode_selected) + sof_lnl_ops.probe = lnl_hda_dsp_probe; /* shutdown */ sof_lnl_ops.shutdown = hda_dsp_shutdown; @@ -106,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) /* pre/post fw run */ sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; - sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run; + sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run; /* parse platform specific extended manifest */ sof_lnl_ops.parse_platform_ext_manifest = NULL; @@ -115,8 +129,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) /* TODO: add core_get and core_put */ /* PM */ - sof_lnl_ops.resume = lnl_hda_dsp_resume; - sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + if (!sdev->dspless_mode_selected) { + sof_lnl_ops.resume = lnl_hda_dsp_resume; + sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + } sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c index 28218766d211..6e3ef0672110 100644 --- a/sound/soc/sof/ipc3-loader.c +++ b/sound/soc/sof/ipc3-loader.c @@ -148,6 +148,8 @@ static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) head = (struct sof_ext_man_header *)fw->data; remaining = head->full_size - head->header_size; + if (remaining < 0 || remaining > sdev->basefw.fw->size) + return -EINVAL; ext_man_size = ipc3_fw_ext_man_size(sdev, fw); /* Assert firmware starts with extended manifest */ diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 330f04bcd75d..35769dd7905e 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -395,6 +395,31 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_SDW: + /* change the default trigger sequence as per HW implementation */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + SND_SOC_DPCM_TRIGGER_POST; + } + + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = + SND_SOC_DPCM_TRIGGER_POST; + } + rate->min = private->dai_config->acp_sdw.rate; + rate->max = private->dai_config->acp_sdw.rate; + channels->min = private->dai_config->acp_sdw.channels; + channels->max = private->dai_config->acp_sdw.channels; + + dev_dbg(component->dev, + "AMD_SDW rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index d47698f4be2d..ab7f46a162da 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -298,6 +298,14 @@ static const struct sof_topology_token micfil_pdm_tokens[] = { offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)}, }; +/* ACP_SDW */ +static const struct sof_topology_token acp_sdw_tokens[] = { + {SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_sdw_params, rate)}, + {SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_sdw_params, channels)}, +}; + /* Core tokens */ static const struct sof_topology_token core_tokens[] = { {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -336,6 +344,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)}, [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens", micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)}, + [SOF_ACP_SDW_TOKENS] = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)}, }; /** @@ -1315,6 +1324,34 @@ static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_ return 0; } +static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* parse the required set of ACP_SDW tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* init IPC */ + config->hdr.size = size; + dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n", + config->acp_sdw.rate, config->acp_sdw.channels); + + /* set config for all DAI's with name matching the link name */ + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1629,6 +1666,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_MEDIATEK_AFE: ret = sof_link_afe_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_SDW: + ret = sof_link_acp_sdw_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 07eb5c6d4adf..0f332c8cdbe6 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -231,9 +231,11 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, */ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, + int direction, struct snd_sof_pcm_stream_pipeline_list *pipeline_list, int state, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; bool allocate, enable, set_fifo_size; struct sof_ipc4_msg msg = {{ 0 }}; int i; @@ -294,6 +296,20 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, msg.extension |= pipeline->msg.extension; } + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + /* + * For ChainDMA the DMA ids are unique with the following mapping: + * playback: 0 - (num_playback_streams - 1) + * capture: num_playback_streams - (num_playback_streams + + * num_capture_streams - 1) + * + * Add the num_playback_streams offset to the DMA ids stored in + * msg.primary in case capture + */ + msg.primary += SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(ipc4_data->num_playback_streams); + msg.primary += SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(ipc4_data->num_playback_streams); + } + if (allocate) msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; @@ -340,7 +356,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * trigger function that handles the rest for the substream. */ if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); + return sof_ipc4_chain_dma_trigger(sdev, substream->stream, + pipeline_list, state, cmd); /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 1d39836d5efa..f3b908b093f9 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -66,6 +66,8 @@ struct sof_ipc4_fw_library { * @nhlt: NHLT table either from the BIOS or the topology manifest * @mtrace_type: mtrace type supported on the booted platform * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply + * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset + * @num_capture_streams: max number of capture DMAs * @max_num_pipelines: max number of pipelines * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware @@ -79,6 +81,8 @@ struct sof_ipc4_fw_data { void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; + int num_playback_streams; + int num_capture_streams; int max_num_pipelines; u32 max_libs_count; bool fw_context_save; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index f779156fe0e6..da4a83afb87a 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -509,6 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_widget *pipe_widget; @@ -548,14 +549,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name, node_type, ipc4_copier->dai_type, ipc4_copier->dai_index); + dai->type = ipc4_copier->dai_type; ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; - if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { - dev_err(scomp->dev, - "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", - ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); + + if (pipeline->use_chain_dma && + !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) { + dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n", + ipc4_copier->dai_type); ret = -ENODEV; goto free_available_fmt; } @@ -598,7 +601,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) } ipc4_copier->copier_config = (uint32_t *)blob; - ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2; + /* set data.gtw_cfg.config_length based on device_count */ + ipc4_copier->data.gtw_cfg.config_length = (sizeof(blob->gw_attr) + + sizeof(blob->alh_cfg.device_count) + + sizeof(*blob->alh_cfg.mapping) * + blob->alh_cfg.device_count) >> 2; break; } case SOF_DAI_INTEL_SSP: @@ -2796,13 +2803,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (!data) return 0; + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); + return 0; + } + switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: - if (pipeline->use_chain_dma) { - pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; - pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); - break; - } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; fallthrough; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 6538d9f4fe96..6cf21e829e07 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -567,6 +567,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, sof_ops(sdev)->set_mach_params(mach, sdev); } +static inline bool +snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported) + return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type); + + return false; +} + /** * snd_sof_dsp_register_poll_timeout - Periodically poll an address * until a condition is met or a timeout occurs diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9163975c9c3f..e693dcb475e4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_pipeline *spipe = swidget->spipe; - struct snd_sof_widget *pipe_widget; int err = 0; int ret; @@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, if (--swidget->use_count) return 0; - pipe_widget = swidget->spipe->pipe_widget; - /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); @@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, * free the scheduler widget (same as pipe_widget) associated with the current swidget. * skip for static pipelines */ - if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free_unlocked(sdev, pipe_widget); + if (swidget->spipe && swidget->dynamic_pipeline_widget && + swidget->id != snd_soc_dapm_scheduler) { + ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); if (ret < 0 && !err) err = ret; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 8874ee5f557f..9ea2ac5adac7 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -276,6 +276,7 @@ enum sof_tokens { SOF_ACPDMIC_TOKENS, SOF_ACPI2S_TOKENS, SOF_MICFIL_TOKENS, + SOF_ACP_SDW_TOKENS, /* this should be the last */ SOF_TOKEN_COUNT, @@ -513,6 +514,7 @@ struct snd_sof_route { struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; + u32 type; int number_configs; int current_config; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6d7897bf9607..d453a4ce3b21 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -157,6 +157,13 @@ struct sof_firmware { u32 payload_offset; }; +enum sof_dai_access { + SOF_DAI_DSP_ACCESS, /* access from DSP only */ + SOF_DAI_HOST_ACCESS, /* access from host only */ + + SOF_DAI_ACCESS_NUM +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU @@ -338,6 +345,8 @@ struct snd_sof_dsp_ops { struct snd_soc_dai_driver *drv; int num_drv; + bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */ + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ u32 hw_info; @@ -595,6 +604,7 @@ struct snd_sof_dev { struct list_head dfsentry_list; bool dbg_dump_printed; bool ipc_dump_printed; + bool d3_prevented; /* runtime pm use count incremented to prevent context lost */ /* firmware loader */ struct sof_ipc_fw_ready fw_ready; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 617a225fff24..bcdb499c96a0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -297,6 +297,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL}, {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, + {"ACP_SDW", SOF_DAI_AMD_SDW}, }; @@ -1968,6 +1969,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ token_id = SOF_MICFIL_TOKENS; num_tuples += token_list[SOF_MICFIL_TOKENS].count; break; + case SOF_DAI_AMD_SDW: + token_id = SOF_ACP_SDW_TOKENS; + num_tuples += token_list[SOF_ACP_SDW_TOKENS].count; + break; default: break; } @@ -2349,25 +2354,43 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { if (WIDGET_IS_DAI(w->id)) { + static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}}; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *priv = &tw->priv; struct snd_sof_widget *swidget; - struct snd_sof_dai dai; + struct snd_sof_dai *sdai; int ret; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); if (!swidget) return -ENOMEM; - memset(&dai, 0, sizeof(dai)); + sdai = kzalloc(sizeof(*sdai), GFP_KERNEL); + if (!sdai) { + kfree(swidget); + return -ENOMEM; + } + + ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name); + kfree(swidget); + kfree(sdai); + return ret; + } - ret = sof_connect_dai_widget(scomp, w, tw, &dai); + ret = sof_connect_dai_widget(scomp, w, tw, sdai); if (ret) { kfree(swidget); + kfree(sdai); return ret; } swidget->scomp = scomp; swidget->widget = w; + swidget->private = sdai; mutex_init(&swidget->setup_mutex); w->dobj.private = swidget; list_add(&swidget->list, &sdev->widget_list); @@ -2391,6 +2414,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp, /* remove and free swidget object */ list_del(&swidget->list); + kfree(swidget->private); kfree(swidget); } diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index b4b158dc736e..d9d1e021f5b2 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -649,7 +649,7 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, * Link 2: McASP10 <- pcm3168a_1 ADC */ comp_count = 6; - compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), + compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent), GFP_KERNEL); if (!compnent) { ret = -ENOMEM; @@ -763,7 +763,7 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, * \ pcm3168a_b ADC */ comp_count = 8; - compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), + compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent), GFP_KERNEL); if (!compnent) { ret = -ENOMEM; diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index 29bff9e6337b..4513b527ab97 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -379,7 +379,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->num_links = 1; card->dev = dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(dev, card); if (ret) { dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); return ret; @@ -393,19 +393,11 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) return 0; } -static void omap_hdmi_audio_remove(struct platform_device *pdev) -{ - struct hdmi_audio_data *ad = platform_get_drvdata(pdev); - - snd_soc_unregister_card(ad->card); -} - static struct platform_driver hdmi_audio_driver = { .driver = { .name = DRV_NAME, }, .probe = omap_hdmi_audio_probe, - .remove_new = omap_hdmi_audio_remove, }; module_platform_driver(hdmi_audio_driver); diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 1e8765d75d8f..5648d744aa79 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -1076,8 +1076,6 @@ out: snd_card_free(card); } -#ifdef CONFIG_PM_SLEEP - static int snd_at73c213_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -1109,18 +1107,13 @@ static int snd_at73c213_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend, +static DEFINE_SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend, snd_at73c213_resume); -#define AT73C213_PM_OPS (&at73c213_pm_ops) - -#else -#define AT73C213_PM_OPS NULL -#endif static struct spi_driver at73c213_driver = { .driver = { .name = "at73c213", - .pm = AT73C213_PM_OPS, + .pm = &at73c213_pm_ops, }, .probe = snd_at73c213_probe, .remove = snd_at73c213_remove, diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 0006c3ddb51d..a82af9374852 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -85,7 +85,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch return -EINVAL; emu->card = card; - emu->name = kstrdup(name, GFP_KERNEL); + emu->name = kstrdup_const(name, GFP_KERNEL); emu->voices = kcalloc(emu->max_voices, sizeof(struct snd_emux_voice), GFP_KERNEL); if (emu->name == NULL || emu->voices == NULL) @@ -140,7 +140,7 @@ int snd_emux_free(struct snd_emux *emu) snd_emux_delete_hwdep(emu); snd_sf_free(emu->sflist); kfree(emu->voices); - kfree(emu->name); + kfree_const(emu->name); kfree(emu); return 0; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 409fc1164694..81256ab56835 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1652,6 +1652,34 @@ static const struct usb_feature_control_info *get_feature_control_info(int contr return NULL; } +static int feature_unit_mutevol_ctl_name(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl, + struct usb_audio_term *iterm, + struct usb_audio_term *oterm) +{ + struct usb_audio_term *aterm, *bterm; + bool output_first; + int len = 0; + + /* + * If the input terminal is USB Streaming, we try getting the name of + * the output terminal first in hopes of getting something more + * descriptive than "PCM". + */ + output_first = iterm && !(iterm->type >> 16) && (iterm->type & 0xff00) == 0x0100; + + aterm = output_first ? oterm : iterm; + bterm = output_first ? iterm : oterm; + + if (aterm) + len = get_term_name(mixer->chip, aterm, kctl->id.name, + sizeof(kctl->id.name), 1); + if (!len && bterm) + len = get_term_name(mixer->chip, bterm, kctl->id.name, + sizeof(kctl->id.name), 1); + return len; +} + static void __build_feature_ctl(struct usb_mixer_interface *mixer, const struct usbmix_name_map *imap, unsigned int ctl_mask, int control, @@ -1733,22 +1761,15 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer, case UAC_FU_MUTE: case UAC_FU_VOLUME: /* - * determine the control name. the rule is: - * - if a name id is given in descriptor, use it. - * - if the connected input can be determined, then use the name - * of terminal type. - * - if the connected output can be determined, use it. - * - otherwise, anonymous name. + * Determine the control name: + * - If a name id is given in descriptor, use it. + * - If input and output terminals are present, try to derive + * the name from either of these. + * - Otherwise, make up a name using the feature unit ID. */ if (!len) { - if (iterm) - len = get_term_name(mixer->chip, iterm, - kctl->id.name, - sizeof(kctl->id.name), 1); - if (!len && oterm) - len = get_term_name(mixer->chip, oterm, - kctl->id.name, - sizeof(kctl->id.name), 1); + len = feature_unit_mutevol_ctl_name(mixer, kctl, iterm, + oterm); if (!len) snprintf(kctl->id.name, sizeof(kctl->id.name), "Feature %d", unitid); diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 6de605a601e5..bd114be537d7 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -173,7 +173,12 @@ /* some gui mixers can't handle negative ctl values */ #define SCARLETT2_VOLUME_BIAS 127 -#define SCARLETT2_GAIN_BIAS 70 + +/* maximum preamp input gain and value + * values are from 0 to 70, preamp gain is from 0 to 69 dB + */ +#define SCARLETT2_MAX_GAIN_VALUE 70 +#define SCARLETT2_MAX_GAIN_DB 69 /* mixer range from -80dB to +6dB in 0.5dB steps */ #define SCARLETT2_MIXER_MIN_DB -80 @@ -284,14 +289,22 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { "Mute Playback Switch", "Dim Playback Switch" }; -/* Autogain Status Values */ -enum { - SCARLETT2_AUTOGAIN_STATUS_STOPPED, - SCARLETT2_AUTOGAIN_STATUS_RUNNING, - SCARLETT2_AUTOGAIN_STATUS_FAILED, - SCARLETT2_AUTOGAIN_STATUS_CANCELLED, - SCARLETT2_AUTOGAIN_STATUS_UNKNOWN, - SCARLETT2_AUTOGAIN_STATUS_COUNT +/* The autogain_status is set based on the autogain_switch and + * raw_autogain_status values. + * + * If autogain_switch is set, autogain_status is set to 0 (Running). + * The other status values are from the raw_autogain_status value + 1. + */ +static const char *const scarlett2_autogain_status_texts[] = { + "Running", + "Success", + "SuccessDRover", + "WarnMinGainLimit", + "FailDRunder", + "FailMaxGainLimit", + "FailClipped", + "Cancelled", + "Invalid" }; /* Power Status Values */ @@ -415,7 +428,7 @@ enum { SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, SCARLETT2_CONFIG_POWER_EXT, - SCARLETT2_CONFIG_POWER_STATUS, + SCARLETT2_CONFIG_POWER_LOW, SCARLETT2_CONFIG_PCM_INPUT_SWITCH, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, SCARLETT2_CONFIG_COUNT @@ -723,8 +736,8 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { [SCARLETT2_CONFIG_POWER_EXT] = { .offset = 0x168 }, - [SCARLETT2_CONFIG_POWER_STATUS] = { - .offset = 0x66 } + [SCARLETT2_CONFIG_POWER_LOW] = { + .offset = 0x16d } } }; @@ -2835,9 +2848,9 @@ static int scarlett2_autogain_is_running(struct scarlett2_data *private) { int i; + /* autogain_status[] is 0 if autogain is running */ for (i = 0; i < private->info->gain_input_count; i++) - if (private->autogain_status[i] == - SCARLETT2_AUTOGAIN_STATUS_RUNNING) + if (!private->autogain_status[i]) return 1; return 0; @@ -2867,25 +2880,25 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) return err; /* Translate autogain_switch and raw_autogain_status into - * autogain_status + * autogain_status. + * + * When autogain_switch[] is set, the status is the first + * element in scarlett2_autogain_status_texts[] (Running). The + * subsequent elements correspond to the status value from the + * device (raw_autogain_status[]) + 1. The last element is + * "Invalid", in case the device reports a status outside the + * range of scarlett2_autogain_status_texts[]. */ for (i = 0; i < info->gain_input_count; i++) if (private->autogain_switch[i]) + private->autogain_status[i] = 0; + else if (raw_autogain_status[i] < + ARRAY_SIZE(scarlett2_autogain_status_texts) - 1) private->autogain_status[i] = - SCARLETT2_AUTOGAIN_STATUS_RUNNING; - else if (raw_autogain_status[i] == 0) - private->autogain_status[i] = - SCARLETT2_AUTOGAIN_STATUS_STOPPED; - else if (raw_autogain_status[i] >= 2 && - raw_autogain_status[i] <= 5) - private->autogain_status[i] = - SCARLETT2_AUTOGAIN_STATUS_FAILED; - else if (raw_autogain_status[i] == 6) - private->autogain_status[i] = - SCARLETT2_AUTOGAIN_STATUS_CANCELLED; + raw_autogain_status[i] + 1; else private->autogain_status[i] = - SCARLETT2_AUTOGAIN_STATUS_UNKNOWN; + ARRAY_SIZE(scarlett2_autogain_status_texts) - 1; return 0; } @@ -3111,12 +3124,10 @@ unlock: static int scarlett2_autogain_status_ctl_info( struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - static const char *const values[SCARLETT2_AUTOGAIN_STATUS_COUNT] = { - "Stopped", "Running", "Failed", "Cancelled", "Unknown" - }; - return snd_ctl_enum_info( - uinfo, 1, SCARLETT2_AUTOGAIN_STATUS_COUNT, values); + uinfo, 1, + ARRAY_SIZE(scarlett2_autogain_status_texts), + scarlett2_autogain_status_texts); } static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = { @@ -3458,7 +3469,7 @@ static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = elem->channels; uinfo->value.integer.min = 0; - uinfo->value.integer.max = SCARLETT2_GAIN_BIAS; + uinfo->value.integer.max = SCARLETT2_MAX_GAIN_VALUE; uinfo->value.integer.step = 1; unlock: @@ -3535,7 +3546,7 @@ unlock: } static const DECLARE_TLV_DB_MINMAX( - db_scale_scarlett2_gain, -SCARLETT2_GAIN_BIAS * 100, 0 + db_scale_scarlett2_gain, 0, SCARLETT2_MAX_GAIN_DB * 100 ); static const struct snd_kcontrol_new scarlett2_input_gain_ctl = { @@ -6294,8 +6305,7 @@ static int scarlett2_update_power_status(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; int err; - u8 power_ext; - u8 power_status; + u8 power_ext, power_low; private->power_status_updated = 0; @@ -6304,12 +6314,12 @@ static int scarlett2_update_power_status(struct usb_mixer_interface *mixer) if (err < 0) return err; - err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_STATUS, - 1, &power_status); + err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_LOW, + 1, &power_low); if (err < 0) return err; - if (power_status > 1) + if (power_low) private->power_status = SCARLETT2_POWER_STATUS_FAIL; else if (power_ext) private->power_status = SCARLETT2_POWER_STATUS_EXT; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 3d4add94e367..d5409f387945 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -300,9 +300,12 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, c = 0; if (bits) { - for (; bits && *maps; maps++, bits >>= 1) + for (; bits && *maps; maps++, bits >>= 1) { if (bits & 1) chmap->map[c++] = *maps; + if (c == chmap->channels) + break; + } } else { /* If we're missing wChannelConfig, then guess something to make sure the channel map is not skipped entirely */ diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile index 2742bddb8874..a839f8c8b5e6 100644 --- a/sound/virtio/Makefile +++ b/sound/virtio/Makefile @@ -7,6 +7,7 @@ virtio_snd-objs := \ virtio_chmap.o \ virtio_ctl_msg.o \ virtio_jack.o \ + virtio_kctl.o \ virtio_pcm.o \ virtio_pcm_msg.o \ virtio_pcm_ops.o diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index b158c3cb8e5f..2da20c625247 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -64,6 +64,9 @@ static void virtsnd_event_dispatch(struct virtio_snd *snd, case VIRTIO_SND_EVT_PCM_XRUN: virtsnd_pcm_event(snd, event); break; + case VIRTIO_SND_EVT_CTL_NOTIFY: + virtsnd_kctl_event(snd, event); + break; } } @@ -233,6 +236,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd) if (rc) return rc; + if (virtio_has_feature(vdev, VIRTIO_SND_F_CTLS)) { + rc = virtsnd_kctl_parse_cfg(snd); + if (rc) + return rc; + } + if (snd->njacks) { rc = virtsnd_jack_build_devs(snd); if (rc) @@ -251,6 +260,12 @@ static int virtsnd_build_devs(struct virtio_snd *snd) return rc; } + if (snd->nkctls) { + rc = virtsnd_kctl_build_devs(snd); + if (rc) + return rc; + } + return snd_card_register(snd->card); } @@ -417,10 +432,16 @@ static const struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_SND_F_CTLS +}; + static struct virtio_driver virtsnd_driver = { .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .validate = virtsnd_validate, .probe = virtsnd_probe, .remove = virtsnd_remove, diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index 86ef3941895e..3ceee4e416fc 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -32,6 +32,16 @@ struct virtio_snd_queue { }; /** + * struct virtio_kctl - VirtIO control element. + * @kctl: ALSA control element. + * @items: Items for the ENUMERATED element type. + */ +struct virtio_kctl { + struct snd_kcontrol *kctl; + struct virtio_snd_ctl_enum_item *items; +}; + +/** * struct virtio_snd - VirtIO sound card device. * @vdev: Underlying virtio device. * @queues: Virtqueue wrappers. @@ -45,6 +55,9 @@ struct virtio_snd_queue { * @nsubstreams: Number of PCM substreams. * @chmaps: VirtIO channel maps. * @nchmaps: Number of channel maps. + * @kctl_infos: VirtIO control element information. + * @kctls: VirtIO control elements. + * @nkctls: Number of control elements. */ struct virtio_snd { struct virtio_device *vdev; @@ -59,6 +72,9 @@ struct virtio_snd { u32 nsubstreams; struct virtio_snd_chmap_info *chmaps; u32 nchmaps; + struct virtio_snd_ctl_info *kctl_infos; + struct virtio_kctl *kctls; + u32 nkctls; }; /* Message completion timeout in milliseconds (module parameter). */ @@ -108,4 +124,10 @@ int virtsnd_chmap_parse_cfg(struct virtio_snd *snd); int virtsnd_chmap_build_devs(struct virtio_snd *snd); +int virtsnd_kctl_parse_cfg(struct virtio_snd *snd); + +int virtsnd_kctl_build_devs(struct virtio_snd *snd); + +void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event); + #endif /* VIRTIO_SND_CARD_H */ diff --git a/sound/virtio/virtio_kctl.c b/sound/virtio/virtio_kctl.c new file mode 100644 index 000000000000..7aa79c05b464 --- /dev/null +++ b/sound/virtio/virtio_kctl.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2022 OpenSynergy GmbH + */ +#include <sound/control.h> +#include <linux/virtio_config.h> + +#include "virtio_card.h" + +/* Map for converting VirtIO types to ALSA types. */ +static const snd_ctl_elem_type_t g_v2a_type_map[] = { + [VIRTIO_SND_CTL_TYPE_BOOLEAN] = SNDRV_CTL_ELEM_TYPE_BOOLEAN, + [VIRTIO_SND_CTL_TYPE_INTEGER] = SNDRV_CTL_ELEM_TYPE_INTEGER, + [VIRTIO_SND_CTL_TYPE_INTEGER64] = SNDRV_CTL_ELEM_TYPE_INTEGER64, + [VIRTIO_SND_CTL_TYPE_ENUMERATED] = SNDRV_CTL_ELEM_TYPE_ENUMERATED, + [VIRTIO_SND_CTL_TYPE_BYTES] = SNDRV_CTL_ELEM_TYPE_BYTES, + [VIRTIO_SND_CTL_TYPE_IEC958] = SNDRV_CTL_ELEM_TYPE_IEC958 +}; + +/* Map for converting VirtIO access rights to ALSA access rights. */ +static const unsigned int g_v2a_access_map[] = { + [VIRTIO_SND_CTL_ACCESS_READ] = SNDRV_CTL_ELEM_ACCESS_READ, + [VIRTIO_SND_CTL_ACCESS_WRITE] = SNDRV_CTL_ELEM_ACCESS_WRITE, + [VIRTIO_SND_CTL_ACCESS_VOLATILE] = SNDRV_CTL_ELEM_ACCESS_VOLATILE, + [VIRTIO_SND_CTL_ACCESS_INACTIVE] = SNDRV_CTL_ELEM_ACCESS_INACTIVE, + [VIRTIO_SND_CTL_ACCESS_TLV_READ] = SNDRV_CTL_ELEM_ACCESS_TLV_READ, + [VIRTIO_SND_CTL_ACCESS_TLV_WRITE] = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE, + [VIRTIO_SND_CTL_ACCESS_TLV_COMMAND] = SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND +}; + +/* Map for converting VirtIO event masks to ALSA event masks. */ +static const unsigned int g_v2a_mask_map[] = { + [VIRTIO_SND_CTL_EVT_MASK_VALUE] = SNDRV_CTL_EVENT_MASK_VALUE, + [VIRTIO_SND_CTL_EVT_MASK_INFO] = SNDRV_CTL_EVENT_MASK_INFO, + [VIRTIO_SND_CTL_EVT_MASK_TLV] = SNDRV_CTL_EVENT_MASK_TLV +}; + +/** + * virtsnd_kctl_info() - Returns information about the control. + * @kcontrol: ALSA control element. + * @uinfo: Element information. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_kctl *kctl = &snd->kctls[kcontrol->private_value]; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int i; + + uinfo->type = g_v2a_type_map[le32_to_cpu(kinfo->type)]; + uinfo->count = le32_to_cpu(kinfo->count); + + switch (uinfo->type) { + case SNDRV_CTL_ELEM_TYPE_INTEGER: + uinfo->value.integer.min = + le32_to_cpu(kinfo->value.integer.min); + uinfo->value.integer.max = + le32_to_cpu(kinfo->value.integer.max); + uinfo->value.integer.step = + le32_to_cpu(kinfo->value.integer.step); + + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + uinfo->value.integer64.min = + le64_to_cpu(kinfo->value.integer64.min); + uinfo->value.integer64.max = + le64_to_cpu(kinfo->value.integer64.max); + uinfo->value.integer64.step = + le64_to_cpu(kinfo->value.integer64.step); + + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + uinfo->value.enumerated.items = + le32_to_cpu(kinfo->value.enumerated.items); + i = uinfo->value.enumerated.item; + if (i >= uinfo->value.enumerated.items) + return -EINVAL; + + strscpy(uinfo->value.enumerated.name, kctl->items[i].item, + sizeof(uinfo->value.enumerated.name)); + + break; + } + + return 0; +} + +/** + * virtsnd_kctl_get() - Read the value from the control. + * @kcontrol: ALSA control element. + * @uvalue: Element value. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + struct virtio_snd_ctl_value *kvalue; + size_t request_size = sizeof(*hdr); + size_t response_size = sizeof(struct virtio_snd_hdr) + sizeof(*kvalue); + unsigned int i; + int rc; + + msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + virtsnd_ctl_msg_ref(msg); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_READ); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + rc = virtsnd_ctl_msg_send_sync(snd, msg); + if (rc) + goto on_failure; + + kvalue = (void *)((u8 *)virtsnd_ctl_msg_response(msg) + + sizeof(struct virtio_snd_hdr)); + + switch (type) { + case VIRTIO_SND_CTL_TYPE_BOOLEAN: + case VIRTIO_SND_CTL_TYPE_INTEGER: + for (i = 0; i < count; ++i) + uvalue->value.integer.value[i] = + le32_to_cpu(kvalue->value.integer[i]); + break; + case VIRTIO_SND_CTL_TYPE_INTEGER64: + for (i = 0; i < count; ++i) + uvalue->value.integer64.value[i] = + le64_to_cpu(kvalue->value.integer64[i]); + break; + case VIRTIO_SND_CTL_TYPE_ENUMERATED: + for (i = 0; i < count; ++i) + uvalue->value.enumerated.item[i] = + le32_to_cpu(kvalue->value.enumerated[i]); + break; + case VIRTIO_SND_CTL_TYPE_BYTES: + memcpy(uvalue->value.bytes.data, kvalue->value.bytes, count); + break; + case VIRTIO_SND_CTL_TYPE_IEC958: + memcpy(&uvalue->value.iec958, &kvalue->value.iec958, + sizeof(uvalue->value.iec958)); + break; + } + +on_failure: + virtsnd_ctl_msg_unref(msg); + + return rc; +} + +/** + * virtsnd_kctl_put() - Write the value to the control. + * @kcontrol: ALSA control element. + * @uvalue: Element value. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_ctl_info *kinfo = + &snd->kctl_infos[kcontrol->private_value]; + unsigned int type = le32_to_cpu(kinfo->type); + unsigned int count = le32_to_cpu(kinfo->count); + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + struct virtio_snd_ctl_value *kvalue; + size_t request_size = sizeof(*hdr) + sizeof(*kvalue); + size_t response_size = sizeof(struct virtio_snd_hdr); + unsigned int i; + + msg = virtsnd_ctl_msg_alloc(request_size, response_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_WRITE); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + kvalue = (void *)((u8 *)hdr + sizeof(*hdr)); + + switch (type) { + case VIRTIO_SND_CTL_TYPE_BOOLEAN: + case VIRTIO_SND_CTL_TYPE_INTEGER: + for (i = 0; i < count; ++i) + kvalue->value.integer[i] = + cpu_to_le32(uvalue->value.integer.value[i]); + break; + case VIRTIO_SND_CTL_TYPE_INTEGER64: + for (i = 0; i < count; ++i) + kvalue->value.integer64[i] = + cpu_to_le64(uvalue->value.integer64.value[i]); + break; + case VIRTIO_SND_CTL_TYPE_ENUMERATED: + for (i = 0; i < count; ++i) + kvalue->value.enumerated[i] = + cpu_to_le32(uvalue->value.enumerated.item[i]); + break; + case VIRTIO_SND_CTL_TYPE_BYTES: + memcpy(kvalue->value.bytes, uvalue->value.bytes.data, count); + break; + case VIRTIO_SND_CTL_TYPE_IEC958: + memcpy(&kvalue->value.iec958, &uvalue->value.iec958, + sizeof(kvalue->value.iec958)); + break; + } + + return virtsnd_ctl_msg_send_sync(snd, msg); +} + +/** + * virtsnd_kctl_tlv_op() - Perform an operation on the control's metadata. + * @kcontrol: ALSA control element. + * @op_flag: Operation code (SNDRV_CTL_TLV_OP_XXX). + * @size: Size of the TLV data in bytes. + * @utlv: TLV data. + * + * Context: Process context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_tlv_op(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *utlv) +{ + struct virtio_snd *snd = kcontrol->private_data; + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + unsigned int *tlv; + struct scatterlist sg; + int rc; + + msg = virtsnd_ctl_msg_alloc(sizeof(*hdr), sizeof(struct virtio_snd_hdr), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + + tlv = kzalloc(size, GFP_KERNEL); + if (!tlv) { + rc = -ENOMEM; + goto on_msg_unref; + } + + sg_init_one(&sg, tlv, size); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->control_id = cpu_to_le32(kcontrol->private_value); + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_READ: + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_READ); + + rc = virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false); + if (!rc) { + if (copy_to_user(utlv, tlv, size)) + rc = -EFAULT; + } + + break; + case SNDRV_CTL_TLV_OP_WRITE: + case SNDRV_CTL_TLV_OP_CMD: + if (op_flag == SNDRV_CTL_TLV_OP_WRITE) + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_TLV_WRITE); + else + hdr->hdr.code = + cpu_to_le32(VIRTIO_SND_R_CTL_TLV_COMMAND); + + if (copy_from_user(tlv, utlv, size)) { + rc = -EFAULT; + goto on_msg_unref; + } else { + rc = virtsnd_ctl_msg_send(snd, msg, &sg, NULL, false); + } + + break; + default: + rc = -EINVAL; + /* We never get here - we listed all values for op_flag */ + WARN_ON(1); + goto on_msg_unref; + } + kfree(tlv); + return rc; + +on_msg_unref: + virtsnd_ctl_msg_unref(msg); + kfree(tlv); + + return rc; +} + +/** + * virtsnd_kctl_get_enum_items() - Query items for the ENUMERATED element type. + * @snd: VirtIO sound device. + * @cid: Control element ID. + * + * This function is called during initial device initialization. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_kctl_get_enum_items(struct virtio_snd *snd, unsigned int cid) +{ + struct virtio_device *vdev = snd->vdev; + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; + struct virtio_kctl *kctl = &snd->kctls[cid]; + struct virtio_snd_msg *msg; + struct virtio_snd_ctl_hdr *hdr; + unsigned int n = le32_to_cpu(kinfo->value.enumerated.items); + struct scatterlist sg; + + msg = virtsnd_ctl_msg_alloc(sizeof(*hdr), + sizeof(struct virtio_snd_hdr), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + kctl->items = devm_kcalloc(&vdev->dev, n, sizeof(*kctl->items), + GFP_KERNEL); + if (!kctl->items) { + virtsnd_ctl_msg_unref(msg); + return -ENOMEM; + } + + sg_init_one(&sg, kctl->items, n * sizeof(*kctl->items)); + + hdr = virtsnd_ctl_msg_request(msg); + hdr->hdr.code = cpu_to_le32(VIRTIO_SND_R_CTL_ENUM_ITEMS); + hdr->control_id = cpu_to_le32(cid); + + return virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false); +} + +/** + * virtsnd_kctl_parse_cfg() - Parse the control element configuration. + * @snd: VirtIO sound device. + * + * This function is called during initial device initialization. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_kctl_parse_cfg(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + u32 i; + int rc; + + virtio_cread_le(vdev, struct virtio_snd_config, controls, + &snd->nkctls); + if (!snd->nkctls) + return 0; + + snd->kctl_infos = devm_kcalloc(&vdev->dev, snd->nkctls, + sizeof(*snd->kctl_infos), GFP_KERNEL); + if (!snd->kctl_infos) + return -ENOMEM; + + snd->kctls = devm_kcalloc(&vdev->dev, snd->nkctls, sizeof(*snd->kctls), + GFP_KERNEL); + if (!snd->kctls) + return -ENOMEM; + + rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CTL_INFO, 0, snd->nkctls, + sizeof(*snd->kctl_infos), snd->kctl_infos); + if (rc) + return rc; + + for (i = 0; i < snd->nkctls; ++i) { + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[i]; + unsigned int type = le32_to_cpu(kinfo->type); + + if (type == VIRTIO_SND_CTL_TYPE_ENUMERATED) { + rc = virtsnd_kctl_get_enum_items(snd, i); + if (rc) + return rc; + } + } + + return 0; +} + +/** + * virtsnd_kctl_build_devs() - Build ALSA control elements. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_kctl_build_devs(struct virtio_snd *snd) +{ + unsigned int cid; + + for (cid = 0; cid < snd->nkctls; ++cid) { + struct virtio_snd_ctl_info *kinfo = &snd->kctl_infos[cid]; + struct virtio_kctl *kctl = &snd->kctls[cid]; + struct snd_kcontrol_new kctl_new; + unsigned int i; + int rc; + + memset(&kctl_new, 0, sizeof(kctl_new)); + + kctl_new.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kctl_new.name = kinfo->name; + kctl_new.index = le32_to_cpu(kinfo->index); + + for (i = 0; i < ARRAY_SIZE(g_v2a_access_map); ++i) + if (le32_to_cpu(kinfo->access) & (1 << i)) + kctl_new.access |= g_v2a_access_map[i]; + + if (kctl_new.access & (SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_WRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND)) { + kctl_new.access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + kctl_new.tlv.c = virtsnd_kctl_tlv_op; + } + + kctl_new.info = virtsnd_kctl_info; + kctl_new.get = virtsnd_kctl_get; + kctl_new.put = virtsnd_kctl_put; + kctl_new.private_value = cid; + + kctl->kctl = snd_ctl_new1(&kctl_new, snd); + if (!kctl->kctl) + return -ENOMEM; + + rc = snd_ctl_add(snd->card, kctl->kctl); + if (rc) + return rc; + } + + return 0; +} + +/** + * virtsnd_kctl_event() - Handle the control element event notification. + * @snd: VirtIO sound device. + * @event: VirtIO sound event. + * + * Context: Interrupt context. + */ +void virtsnd_kctl_event(struct virtio_snd *snd, struct virtio_snd_event *event) +{ + struct virtio_snd_ctl_event *kevent = + (struct virtio_snd_ctl_event *)event; + struct virtio_kctl *kctl; + unsigned int cid = le16_to_cpu(kevent->control_id); + unsigned int mask = 0; + unsigned int i; + + if (cid >= snd->nkctls) + return; + + for (i = 0; i < ARRAY_SIZE(g_v2a_mask_map); ++i) + if (le16_to_cpu(kevent->mask) & (1 << i)) + mask |= g_v2a_mask_map[i]; + + + kctl = &snd->kctls[cid]; + + snd_ctl_notify(snd->card, mask, &kctl->kctl->id); +} |