diff options
author | Takashi Iwai <tiwai@suse.de> | 2024-07-15 16:31:00 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2024-07-15 16:31:00 +0200 |
commit | 97b10a77b150df664bb521ae217dd6bceb40d5d3 (patch) | |
tree | 9f14e5d6e0a9e048cfef95f41678569dd994aba6 | |
parent | e54dc34318f62bdeea7edefed386d47fae155618 (diff) | |
parent | c51cba4755609ad97ba97713210c16f043c73224 (diff) | |
download | lwn-97b10a77b150df664bb521ae217dd6bceb40d5d3.tar.gz lwn-97b10a77b150df664bb521ae217dd6bceb40d5d3.zip |
Merge tag 'asoc-v6.11' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for for v6.11
There are a lot of changes in here, though the big bulk of things is
cleanups and simplifications of various kinds which are internally
rather than externally visible. A good chunk of those are DT schema
conversions, but there's also a lot of changes in the code.
Highlights:
- Syncing of features between simple-audio-card and the two
audio-graph cards so there is no reason to stick with an older
driver.
- Support for specifying the order of operations for components within
cards to allow quirking for unusual systems.
- New support for Asahi Kasei AK4619, Cirrus Logic CS530x, Everest
Semiconductors ES8311, NXP i.MX95 and LPC32xx, Qualcomm LPASS v2.5
and WCD937x, Realtek RT1318 and RT1320 and Texas Instruments PCM5242.
661 files changed, 23899 insertions, 5937 deletions
diff --git a/Documentation/admin-guide/LSM/tomoyo.rst b/Documentation/admin-guide/LSM/tomoyo.rst index 4bc9c2b4da6f..bdb2c2e2a1b2 100644 --- a/Documentation/admin-guide/LSM/tomoyo.rst +++ b/Documentation/admin-guide/LSM/tomoyo.rst @@ -9,8 +9,8 @@ TOMOYO is a name-based MAC extension (LSM module) for the Linux kernel. LiveCD-based tutorials are available at -http://tomoyo.sourceforge.jp/1.8/ubuntu12.04-live.html -http://tomoyo.sourceforge.jp/1.8/centos6-live.html +https://tomoyo.sourceforge.net/1.8/ubuntu12.04-live.html +https://tomoyo.sourceforge.net/1.8/centos6-live.html Though these tutorials use non-LSM version of TOMOYO, they are useful for you to know what TOMOYO is. @@ -21,45 +21,32 @@ How to enable TOMOYO? Build the kernel with ``CONFIG_SECURITY_TOMOYO=y`` and pass ``security=tomoyo`` on kernel's command line. -Please see http://tomoyo.osdn.jp/2.5/ for details. +Please see https://tomoyo.sourceforge.net/2.6/ for details. Where is documentation? ======================= User <-> Kernel interface documentation is available at -https://tomoyo.osdn.jp/2.5/policy-specification/index.html . +https://tomoyo.sourceforge.net/2.6/policy-specification/index.html . Materials we prepared for seminars and symposiums are available at -https://osdn.jp/projects/tomoyo/docs/?category_id=532&language_id=1 . +https://sourceforge.net/projects/tomoyo/files/docs/ . Below lists are chosen from three aspects. What is TOMOYO? TOMOYO Linux Overview - https://osdn.jp/projects/tomoyo/docs/lca2009-takeda.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lca2009-takeda.pdf TOMOYO Linux: pragmatic and manageable security for Linux - https://osdn.jp/projects/tomoyo/docs/freedomhectaipei-tomoyo.pdf + https://sourceforge.net/projects/tomoyo/files/docs/freedomhectaipei-tomoyo.pdf TOMOYO Linux: A Practical Method to Understand and Protect Your Own Linux Box - https://osdn.jp/projects/tomoyo/docs/PacSec2007-en-no-demo.pdf + https://sourceforge.net/projects/tomoyo/files/docs/PacSec2007-en-no-demo.pdf What can TOMOYO do? Deep inside TOMOYO Linux - https://osdn.jp/projects/tomoyo/docs/lca2009-kumaneko.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lca2009-kumaneko.pdf The role of "pathname based access control" in security. - https://osdn.jp/projects/tomoyo/docs/lfj2008-bof.pdf + https://sourceforge.net/projects/tomoyo/files/docs/lfj2008-bof.pdf History of TOMOYO? Realities of Mainlining - https://osdn.jp/projects/tomoyo/docs/lfj2008.pdf - -What is future plan? -==================== - -We believe that inode based security and name based security are complementary -and both should be used together. But unfortunately, so far, we cannot enable -multiple LSM modules at the same time. We feel sorry that you have to give up -SELinux/SMACK/AppArmor etc. when you want to use TOMOYO. - -We hope that LSM becomes stackable in future. Meanwhile, you can use non-LSM -version of TOMOYO, available at http://tomoyo.osdn.jp/1.8/ . -LSM version of TOMOYO is a subset of non-LSM version of TOMOYO. We are planning -to port non-LSM version's functionalities to LSM versions. + https://sourceforge.net/projects/tomoyo/files/docs/lfj2008.pdf diff --git a/Documentation/admin-guide/mm/transhuge.rst b/Documentation/admin-guide/mm/transhuge.rst index 076443cc10a6..d414d3f5592a 100644 --- a/Documentation/admin-guide/mm/transhuge.rst +++ b/Documentation/admin-guide/mm/transhuge.rst @@ -467,11 +467,11 @@ anon_fault_fallback_charge instead falls back to using huge pages with lower orders or small pages even though the allocation was successful. -anon_swpout +swpout is incremented every time a huge page is swapped out in one piece without splitting. -anon_swpout_fallback +swpout_fallback is incremented if a huge page has to be split before swapout. Usually because failed to allocate some continuous swap space for the huge page. diff --git a/Documentation/cdrom/cdrom-standard.rst b/Documentation/cdrom/cdrom-standard.rst index 7964fe134277..6c1303cff159 100644 --- a/Documentation/cdrom/cdrom-standard.rst +++ b/Documentation/cdrom/cdrom-standard.rst @@ -217,7 +217,7 @@ current *struct* is:: int (*media_changed)(struct cdrom_device_info *, int); int (*tray_move)(struct cdrom_device_info *, int); int (*lock_door)(struct cdrom_device_info *, int); - int (*select_speed)(struct cdrom_device_info *, int); + int (*select_speed)(struct cdrom_device_info *, unsigned long); int (*get_last_session) (struct cdrom_device_info *, struct cdrom_multisession *); int (*get_mcn)(struct cdrom_device_info *, struct cdrom_mcn *); @@ -396,7 +396,7 @@ action need be taken, and the return value should be 0. :: - int select_speed(struct cdrom_device_info *cdi, int speed) + int select_speed(struct cdrom_device_info *cdi, unsigned long speed) Some CD-ROM drives are capable of changing their head-speed. There are several reasons for changing the speed of a CD-ROM drive. Badly diff --git a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml index d2dce238ff5d..3e996346b264 100644 --- a/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml +++ b/Documentation/devicetree/bindings/arm/stm32/st,mlahb.yaml @@ -54,11 +54,10 @@ unevaluatedProperties: false examples: - | - mlahb: ahb@38000000 { + ahb { compatible = "st,mlahb", "simple-bus"; #address-cells = <1>; #size-cells = <1>; - reg = <0x10000000 0x40000>; ranges; dma-ranges = <0x00000000 0x38000000 0x10000>, <0x10000000 0x10000000 0x60000>, diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml index c6d0d8d81ed4..c2a158b75e49 100644 --- a/Documentation/devicetree/bindings/arm/sunxi.yaml +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml @@ -57,17 +57,17 @@ properties: - const: allwinner,sun8i-v3s - description: Anbernic RG35XX (2024) - - items: + items: - const: anbernic,rg35xx-2024 - const: allwinner,sun50i-h700 - description: Anbernic RG35XX Plus - - items: + items: - const: anbernic,rg35xx-plus - const: allwinner,sun50i-h700 - description: Anbernic RG35XX H - - items: + items: - const: anbernic,rg35xx-h - const: allwinner,sun50i-h700 diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index dc4ac41f2441..a62916d07a08 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -18,9 +18,12 @@ allOf: properties: compatible: - enum: - - elan,ekth6915 - - ilitek,ili2901 + oneOf: + - items: + - enum: + - elan,ekth5015m + - const: elan,ekth6915 + - const: elan,ekth6915 reg: const: 0x10 @@ -33,6 +36,12 @@ properties: reset-gpios: description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. + no-reset-on-power-off: + type: boolean + description: + Reset line is wired so that it can (and should) be left deasserted when + the power supply is off. + vcc33-supply: description: The 3.3V supply to the touchscreen. @@ -58,8 +67,8 @@ examples: #address-cells = <1>; #size-cells = <0>; - ap_ts: touchscreen@10 { - compatible = "elan,ekth6915"; + touchscreen@10 { + compatible = "elan,ekth5015m", "elan,ekth6915"; reg = <0x10>; interrupt-parent = <&tlmm>; diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml b/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml new file mode 100644 index 000000000000..1abeec768d79 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ilitek,ili2901.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/ilitek,ili2901.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI2901 touchscreen controller + +maintainers: + - Jiri Kosina <jkosina@suse.com> + +description: + Supports the Ilitek ILI2901 touchscreen controller. + This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + +properties: + compatible: + enum: + - ilitek,ili2901 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + panel: true + + reset-gpios: + maxItems: 1 + + vcc33-supply: true + + vccio-supply: true + +required: + - compatible + - reg + - interrupts + - vcc33-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@41 { + compatible = "ilitek,ili2901"; + reg = <0x41>; + + interrupt-parent = <&tlmm>; + interrupts = <9 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_ts>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt deleted file mode 100644 index ae5f7f057dc3..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4104.txt +++ /dev/null @@ -1,25 +0,0 @@ -AK4104 S/PDIF transmitter - -This device supports SPI mode only. - -Required properties: - - - compatible : "asahi-kasei,ak4104" - - - reg : The chip select number on the SPI bus - - - vdd-supply : A regulator node, providing 2.7V - 3.6V - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the device starts. - -Example: - -spdif: ak4104@0 { - compatible = "asahi-kasei,ak4104"; - reg = <0>; - spi-max-frequency = <5000000>; - vdd-supply = <&vdd_3v3_reg>; -}; diff --git a/Documentation/devicetree/bindings/sound/ak4554.txt b/Documentation/devicetree/bindings/sound/ak4554.txt deleted file mode 100644 index 934fa02754b3..000000000000 --- a/Documentation/devicetree/bindings/sound/ak4554.txt +++ /dev/null @@ -1,11 +0,0 @@ -AK4554 ADC/DAC - -Required properties: - - - compatible : "asahi-kasei,ak4554" - -Example: - -ak4554-adc-dac { - compatible = "asahi-kasei,ak4554"; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt deleted file mode 100644 index 4e8cd7eb7cec..000000000000 --- a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.txt +++ /dev/null @@ -1,58 +0,0 @@ -* Amlogic HDMI Tx control glue - -Required properties: -- compatible: "amlogic,g12a-tohdmitx" or - "amlogic,sm1-tohdmitx" -- reg: physical base address of the controller and length of memory - mapped region. -- #sound-dai-cells: should be 1. -- resets: phandle to the dedicated reset line of the hdmitx glue. - -Example on the S905X2 SoC: - -tohdmitx: audio-controller@744 { - compatible = "amlogic,g12a-tohdmitx"; - reg = <0x0 0x744 0x0 0x4>; - #sound-dai-cells = <1>; - resets = <&clkc_audio AUD_RESET_TOHDMITX>; -}; - -Example of an 'amlogic,axg-sound-card': - -sound { - compatible = "amlogic,axg-sound-card"; - -[...] - - dai-link-x { - sound-dai = <&tdmif_a>; - dai-format = "i2s"; - dai-tdm-slot-tx-mask-0 = <1 1>; - - codec-0 { - sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>; - }; - - codec-1 { - sound-dai = <&external_dac>; - }; - }; - - dai-link-y { - sound-dai = <&tdmif_c>; - dai-format = "i2s"; - dai-tdm-slot-tx-mask-0 = <1 1>; - - codec { - sound-dai = <&tohdmitx TOHDMITX_I2S_IN_C>; - }; - }; - - dai-link-z { - sound-dai = <&tohdmitx TOHDMITX_I2S_OUT>; - - codec { - sound-dai = <&hdmi_tx>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml new file mode 100644 index 000000000000..b4b78475c5b8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-tohdmitx.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,g12a-tohdmitx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic G12a HDMI TX Control Glue + +maintainers: + - Jerome Brunet <jbrunet@baylibre.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + $nodename: + pattern: "^audio-controller@.*" + + compatible: + oneOf: + - items: + - const: amlogic,g12a-tohdmitx + - items: + - enum: + - amlogic,sm1-tohdmitx + - const: amlogic,g12a-tohdmitx + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + "#sound-dai-cells": + const: 1 + +required: + - compatible + - reg + - resets + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h> + + tohdmitx: audio-controller@744 { + compatible = "amlogic,g12a-tohdmitx"; + reg = <0x744 0x4>; + resets = <&clkc_audio AUD_RESET_TOHDMITX>; + #sound-dai-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index d4277d342e69..0ecdaf7190e9 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -23,7 +23,6 @@ properties: audio-widgets: $ref: /schemas/types.yaml#/definitions/non-unique-string-array - minItems: 2 description: |- A list off component DAPM widget. Each entry is a pair of strings, the first being the widget type, the second being the widget name diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml new file mode 100644 index 000000000000..86f6061d3c50 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4104.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4104.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4104 S/PDIF transmitter + +allOf: + - $ref: dai-common.yaml# + +maintainers: + - Daniel Mack <github@zonque.org> + - Xiaxi Shen <shenxiaxi26@gmail.com> + +properties: + compatible: + const: asahi-kasei,ak4104 + + reg: + description: Chip select number on the SPI bus + maxItems: 1 + + vdd-supply: + description: A regulator node providing between 2.7V and 3.6V. + + reset-gpios: + maxItems: 1 + description: Optional GPIO spec for the reset pin, deasserted + before communication starts. + +required: + - compatible + - reg + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@0 { + compatible = "asahi-kasei,ak4104"; + reg = <0>; + vdd-supply = <&vdd_3v3_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4375.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml index 587598e122c6..bc07fcba535b 100644 --- a/Documentation/devicetree/bindings/sound/ak4375.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4375.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4375.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4375 DAC and headphones amplifier diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml new file mode 100644 index 000000000000..c77d85df239e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4554.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4554.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4554 sound codec + +maintainers: + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + - Liam Girdwood <lgirdwood@gmail.com> + - Mark Brown <broonie@kernel.org> + +properties: + compatible: + const: asahi-kasei,ak4554 + +required: + - compatible + +additionalProperties: false + +examples: + - | + codec { + compatible = "asahi-kasei,ak4554"; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4613.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml index 75e13414d6eb..b49a6cff9f1f 100644 --- a/Documentation/devicetree/bindings/sound/ak4613.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4613.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4613.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4613 I2C transmitter diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml new file mode 100644 index 000000000000..d412531ef9a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4619.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4619 I2C transmitter + +maintainers: + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + - Khanh Le <khanh.le.xr@renesas.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: asahi-kasei,ak4619 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: mclk + + "#sound-dai-cells": + const: 0 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@10 { + compatible = "asahi-kasei,ak4619"; + reg = <0x10>; + + clocks = <&rcar_sound>; + clock-names = "mclk"; + + #sound-dai-cells = <0>; + port { + ak4619_endpoint: endpoint { + remote-endpoint = <&rsnd_endpoint>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4642.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml index 437fe5d7cae1..fc03f0373a1a 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4642.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4642.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4642 I2C transmitter diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml index d3ce4de449d5..f943f90d8b15 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml @@ -23,6 +23,11 @@ properties: 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 + aux-devs: + description: | + List of phandles pointing to auxiliary devices, such + as amplifiers, to be added to the sound card. + $ref: /schemas/types.yaml#/definitions/phandle-array multi: type: object description: Multi-CPU/Codec node diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 28b27e7e45de..d1cbfc5edd3a 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -25,6 +25,15 @@ definitions: capture-only: description: port connection used only for capture $ref: /schemas/types.yaml#/definitions/flag + link-trigger-order: + description: trigger order for both start/stop + $ref: /schemas/types.yaml#/definitions/uint32-array + link-trigger-order-start: + description: trigger order for start + $ref: /schemas/types.yaml#/definitions/uint32-array + link-trigger-order-stop: + description: trigger order for stop + $ref: /schemas/types.yaml#/definitions/uint32-array endpoint-base: allOf: diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml new file mode 100644 index 000000000000..336e11773694 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs4270.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS4270 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: + The CS4270 is a stereo audio codec. The driver for this device currently only + supports I2C. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: cirrus,cs4270 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + reset-gpios: + description: + This pin will be deasserted before communication to the codec starts. + maxItems: 1 + + va-supply: + description: Analog power supply. + + vd-supply: + description: Digital power supply. + + vlc-supply: + description: Serial Control Port power supply. + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@48 { + compatible = "cirrus,cs4270"; + reg = <0x48>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml new file mode 100644 index 000000000000..725b47e82062 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42xx8.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42448/CS42888 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + enum: + - cirrus,cs42448 + - cirrus,cs42888 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + const: mclk + + VA-supply: + description: Analog power supply. + + VD-supply: + description: Digital power supply. + + VLC-supply: + description: Control port power supply + + VLS-supply: + description: Serial port interface power supply. + + reset-gpios: + description: This pin is connected to the chip's RESET pin. + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + +if: + properties: + compatible: + contains: + const: cirrus,cs42888 +then: + required: + - VA-supply + - VD-supply + - VLC-supply + - VLS-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@48 { + compatible = "cirrus,cs42888"; + reg = <0x48>; + clocks = <&codec_mclk 0>; + clock-names = "mclk"; + VA-supply = <®_audio>; + VD-supply = <®_audio>; + VLS-supply = <®_audio>; + VLC-supply = <®_audio>; + reset-gpios = <&gpio 1>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml new file mode 100644 index 000000000000..9582eb8eb418 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs530x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic cs530x family of audio ADCs + +maintainers: + - Paul Handrigan <paulha@opensource.cirrus.com> + - patches@opensource.cirrus.com + +description: + The CS530X devices are a family of high performance audio ADCs. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs5302 + - cirrus,cs5304 + - cirrus,cs5308 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 1 + + reset-gpios: + maxItems: 1 + + vdd-a-supply: + description: Analog power supply + + vdd-io-supply: + description: Digital IO power supply + + cirrus,in-hiz-pin12: + description: + Sets input channels one and two to high impedance. + type: boolean + + cirrus,in-hiz-pin34: + description: + Sets input channels three and four to high impedance. + type: boolean + + cirrus,in-hiz-pin56: + description: + Sets input channels five and six to high impedance. + type: boolean + + cirrus,in-hiz-pin78: + description: + Sets input channels seven and eight to high impedance. + type: boolean + +required: + - compatible + - reg + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs5304: adc@48 { + compatible = "cirrus,cs5304"; + reg = <0x48>; + #sound-dai-cells = <1>; + reset-gpios = <&gpio 110 GPIO_ACTIVE_LOW>; + vdd-a-supply = <&vreg>; + vdd-io-supply = <&vreg>; + cirrus,in-hiz-pin34; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs4270.txt b/Documentation/devicetree/bindings/sound/cs4270.txt deleted file mode 100644 index c33770ec4c3c..000000000000 --- a/Documentation/devicetree/bindings/sound/cs4270.txt +++ /dev/null @@ -1,21 +0,0 @@ -CS4270 audio CODEC - -The driver for this device currently only supports I2C. - -Required properties: - - - compatible : "cirrus,cs4270" - - - reg : the I2C address of the device for I2C - -Optional properties: - - - reset-gpios : a GPIO spec for the reset pin. If specified, it will be - deasserted before communication to the codec starts. - -Example: - -codec: cs4270@48 { - compatible = "cirrus,cs4270"; - reg = <0x48>; -}; diff --git a/Documentation/devicetree/bindings/sound/cs42xx8.txt b/Documentation/devicetree/bindings/sound/cs42xx8.txt deleted file mode 100644 index bbfe39347c20..000000000000 --- a/Documentation/devicetree/bindings/sound/cs42xx8.txt +++ /dev/null @@ -1,34 +0,0 @@ -CS42448/CS42888 audio CODEC - -Required properties: - - - compatible : must contain one of "cirrus,cs42448" and "cirrus,cs42888" - - - reg : the I2C address of the device for I2C - - - clocks : a list of phandles + clock-specifiers, one for each entry in - clock-names - - - clock-names : must contain "mclk" - - - VA-supply, VD-supply, VLS-supply, VLC-supply: power supplies for the device, - as covered in Documentation/devicetree/bindings/regulator/regulator.txt - -Optional properties: - - - reset-gpios : a GPIO spec to define which pin is connected to the chip's - !RESET pin - -Example: - -cs42888: codec@48 { - compatible = "cirrus,cs42888"; - reg = <0x48>; - clocks = <&codec_mclk 0>; - clock-names = "mclk"; - VA-supply = <®_audio>; - VD-supply = <®_audio>; - VLS-supply = <®_audio>; - VLC-supply = <®_audio>; - reset-gpios = <&pca9557_b 1 GPIO_ACTIVE_LOW>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es7134.txt b/Documentation/devicetree/bindings/sound/everest,es7134.txt deleted file mode 100644 index 091666069bde..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7134.txt +++ /dev/null @@ -1,15 +0,0 @@ -ES7134 i2s DA converter - -Required properties: -- compatible : "everest,es7134" or - "everest,es7144" or - "everest,es7154" -- VDD-supply : regulator phandle for the VDD supply -- PVDD-supply: regulator phandle for the PVDD supply for the es7154 - -Example: - -i2s_codec: external-codec { - compatible = "everest,es7134"; - VDD-supply = <&vcc_5v>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es71x4.yaml b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml new file mode 100644 index 000000000000..fd1b32812228 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es71x4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7134/7144/7154 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +properties: + compatible: + enum: + - everest,es7134 + - everest,es7144 + - everest,es7154 + + VDD-supply: true + PVDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDD-supply + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - everest,es7134 + - everest,es7144 + then: + properties: + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - everest,es7154 + then: + required: + - PVDD-supply + +unevaluatedProperties: false + +examples: + - | + codec { + compatible = "everest,es7134"; + #sound-dai-cells = <0>; + VDD-supply = <&vdd_supply>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.txt b/Documentation/devicetree/bindings/sound/everest,es7241.txt deleted file mode 100644 index 28f82cf4959f..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7241.txt +++ /dev/null @@ -1,28 +0,0 @@ -ES7241 i2s AD converter - -Required properties: -- compatible : "everest,es7241" -- VDDP-supply: regulator phandle for the VDDA supply -- VDDA-supply: regulator phandle for the VDDP supply -- VDDD-supply: regulator phandle for the VDDD supply - -Optional properties: -- reset-gpios: gpio connected to the reset pin -- m0-gpios : gpio connected to the m0 pin -- m1-gpios : gpio connected to the m1 pin -- everest,sdout-pull-down: - Format used by the serial interface is controlled by pulling - the sdout. If the sdout is pulled down, leftj format is used. - If this property is not provided, sdout is assumed to pulled - up and i2s format is used - -Example: - -linein: audio-codec@2 { - #sound-dai-cells = <0>; - compatible = "everest,es7241"; - VDDA-supply = <&vcc_3v3>; - VDDP-supply = <&vcc_3v3>; - VDDD-supply = <&vcc_3v3>; - reset-gpios = <&gpio GPIOH_42>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.yaml b/Documentation/devicetree/bindings/sound/everest,es7241.yaml new file mode 100644 index 000000000000..f179af758730 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es7241.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es7241.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7241 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +properties: + compatible: + enum: + - everest,es7241 + + reset-gpios: + maxItems: 1 + description: GPIO connected to the reset pin + + m0-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + m1-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + everest,sdout-pull-down: + type: boolean + description: + Format used by the serial interface is controlled by pulling + the sdout. If the sdout is pulled down, leftj format is used. + If this property is not provided, sdout is assumed to pulled + up and i2s format is used + + VDDP-supply: true + VDDA-supply: true + VDDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDDP-supply + - VDDA-supply + - VDDD-supply + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + codec { + compatible = "everest,es7241"; + #sound-dai-cells = <0>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + VDDP-supply = <&vddp_supply>; + VDDA-supply = <&vdda_supply>; + VDDD-supply = <&vddd_supply>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml index b6079b3c440d..214f135b7777 100644 --- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml +++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml @@ -4,18 +4,21 @@ $id: http://devicetree.org/schemas/sound/everest,es8316.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Everest ES8316 audio CODEC +title: Everest ES8311 and ES8316 audio CODECs maintainers: - Daniel Drake <drake@endlessm.com> - Katsuhiro Suzuki <katsuhiro@katsuster.net> + - Matteo Martelli <matteomartelli3@gmail.com> allOf: - $ref: dai-common.yaml# properties: compatible: - const: everest,es8316 + enum: + - everest,es8311 + - everest,es8316 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml deleted file mode 100644 index 5fc543d02ecb..000000000000 --- a/Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/fsl,imx-audio-spdif.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Freescale i.MX audio complex with S/PDIF transceiver - -maintainers: - - Shengjiu Wang <shengjiu.wang@nxp.com> - -properties: - compatible: - oneOf: - - items: - - enum: - - fsl,imx-sabreauto-spdif - - fsl,imx6sx-sdb-spdif - - const: fsl,imx-audio-spdif - - enum: - - fsl,imx-audio-spdif - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - - spdif-controller: - $ref: /schemas/types.yaml#/definitions/phandle - description: The phandle of the i.MX S/PDIF controller - - spdif-out: - type: boolean - description: - If present, the transmitting function of S/PDIF will be enabled, - indicating there's a physical S/PDIF out connector or jack on the - board or it's connecting to some other IP block, such as an HDMI - encoder or display-controller. - - spdif-in: - type: boolean - description: - If present, the receiving function of S/PDIF will be enabled, - indicating there is a physical S/PDIF in connector/jack on the board. - -required: - - compatible - - model - - spdif-controller - -anyOf: - - required: - - spdif-in - - required: - - spdif-out - -additionalProperties: false - -examples: - - | - sound-spdif { - compatible = "fsl,imx-audio-spdif"; - model = "imx-spdif"; - spdif-controller = <&spdif>; - spdif-out; - spdif-in; - }; diff --git a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml index 8b33353a80ca..030ccc173130 100644 --- a/Documentation/devicetree/bindings/sound/fsl,mqs.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,mqs.yaml @@ -23,6 +23,8 @@ properties: - fsl,imx8qm-mqs - fsl,imx8qxp-mqs - fsl,imx93-mqs + - fsl,imx95-aonmix-mqs + - fsl,imx95-netcmix-mqs clocks: minItems: 1 diff --git a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml index b522ed7dcc51..a23e49198c37 100644 --- a/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,qmc-audio.yaml @@ -12,7 +12,9 @@ maintainers: description: | The QMC audio is an ASoC component which uses QMC (QUICC Multichannel Controller) channels to transfer the audio data. - It provides as many DAI as the number of QMC channel used. + It provides several DAIs. For each DAI, the DAI is working in interleaved mode + if only one QMC channel is used by the DAI or it is working in non-interleaved + mode if several QMC channels are used by the DAI. allOf: - $ref: dai-common.yaml# @@ -45,12 +47,19 @@ patternProperties: fsl,qmc-chan: $ref: /schemas/types.yaml#/definitions/phandle-array items: - - items: - - description: phandle to QMC node - - description: Channel number + items: + - description: phandle to QMC node + - description: Channel number + minItems: 1 description: - Should be a phandle/number pair. The phandle to QMC node and the QMC - channel to use for this DAI. + Should be a phandle/number pair list. The list of phandle to QMC node + and the QMC channel pair to use for this DAI. + If only one phandle/number pair is provided, this DAI works in + interleaved mode, i.e. audio channels for this DAI are interleaved in + the QMC channel. If more than one pair is provided, this DAI works + in non-interleave mode. In that case the first audio channel uses the + the first QMC channel, the second audio channel uses the second QMC + channel, etc... required: - reg @@ -79,6 +88,11 @@ examples: reg = <17>; fsl,qmc-chan = <&qmc 17>; }; + dai@18 { + reg = <18>; + /* Non-interleaved mode */ + fsl,qmc-chan = <&qmc 18>, <&qmc 19>; + }; }; sound { @@ -115,4 +129,19 @@ examples: dai-tdm-slot-rx-mask = <0 0 1 0 1 0 1 0 1>; }; }; + simple-audio-card,dai-link@2 { + reg = <2>; + format = "dsp_b"; + cpu { + sound-dai = <&audio_controller 18>; + }; + codec { + sound-dai = <&codec3>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <8>; + /* TS 9, 10 */ + dai-tdm-slot-tx-mask = <0 0 0 0 0 0 0 0 0 1 1>; + dai-tdm-slot-rx-mask = <0 0 0 0 0 0 0 0 0 1 1>; + }; + }; }; diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index 188f38baddec..3d5d435c765b 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -29,6 +29,7 @@ properties: - fsl,imx8mp-rpmsg-audio - fsl,imx8ulp-rpmsg-audio - fsl,imx93-rpmsg-audio + - fsl,imx95-rpmsg-audio clocks: items: diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.yaml b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml index 1353c051488f..c6ab1ca16763 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/sgtl5000.yaml# +$id: http://devicetree.org/schemas/sound/fsl,sgtl5000.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale SGTL5000 Stereo Codec diff --git a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml index 0eb0c1ba8710..5e2801014221 100644 --- a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml @@ -22,6 +22,7 @@ properties: enum: - fsl,imx8mp-xcvr - fsl,imx93-xcvr + - fsl,imx95-xcvr reg: items: @@ -41,6 +42,7 @@ properties: items: - description: WAKEUPMIX Audio XCVR Interrupt 1 - description: WAKEUPMIX Audio XCVR Interrupt 2 + - description: SPDIF wakeup interrupt from PHY minItems: 1 clocks: @@ -49,6 +51,9 @@ properties: - description: PHY clock - description: SPBA clock - description: PLL clock + - description: PLL clock source for 8kHz series + - description: PLL clock source for 11kHz series + minItems: 4 clock-names: items: @@ -56,6 +61,9 @@ properties: - const: phy - const: spba - const: pll_ipg + - const: pll8k + - const: pll11k + minItems: 4 dmas: items: @@ -79,15 +87,25 @@ required: - clock-names - dmas - dma-names - - resets allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + const: fsl,imx8mp-xcvr + then: + required: + - resets + - if: properties: compatible: contains: enum: - fsl,imx93-xcvr + - fsl,imx95-xcvr then: properties: interrupts: @@ -96,9 +114,24 @@ allOf: else: properties: interrupts: - maxItems: 1 + minItems: 3 + maxItems: 3 + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8mp-xcvr + - fsl,imx93-xcvr + then: + properties: + clocks: + maxItems: 4 + clock-names: + maxItems: 4 -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -113,7 +146,9 @@ examples: <0x30cc0c00 0x080>, <0x30cc0e00 0x080>; reg-names = "ram", "regs", "rxfifo", "txfifo"; - interrupts = <0x0 128 IRQ_TYPE_LEVEL_HIGH>; + interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>; clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>, <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>, <&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>, diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml index 9922664d5ccc..92aa47ec72c7 100644 --- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml +++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml @@ -67,6 +67,11 @@ properties: - fsl,imx-audio-wm8962 - items: - enum: + - fsl,imx-sabreauto-spdif + - fsl,imx6sx-sdb-spdif + - const: fsl,imx-audio-spdif + - items: + - enum: - fsl,imx-audio-ac97 - fsl,imx-audio-cs42888 - fsl,imx-audio-cs427x @@ -81,6 +86,7 @@ properties: - fsl,imx-audio-wm8960 - fsl,imx-audio-wm8962 - fsl,imx-audio-wm8958 + - fsl,imx-audio-spdif model: $ref: /schemas/types.yaml#/definitions/string @@ -93,8 +99,15 @@ properties: need to add ASRC support via DPCM. audio-codec: - $ref: /schemas/types.yaml#/definitions/phandle - description: The phandle of an audio codec + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + The phandle of an audio codec. + With "fsl,imx-audio-spdif", either SPDIF audio codec spdif_transmitter, + spdif_receiver or both. + minItems: 1 + maxItems: 2 + items: + maxItems: 1 audio-cpu: $ref: /schemas/types.yaml#/definitions/phandle @@ -150,8 +163,10 @@ properties: description: dai-link uses bit clock inversion. mclk-id: - $ref: /schemas/types.yaml#/definitions/uint32 - description: main clock id, specific for each card configuration. + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Main clock id for each codec, specific for each card configuration. + minItems: 1 + maxItems: 2 mux-int-port: $ref: /schemas/types.yaml#/definitions/uint32 @@ -167,6 +182,27 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of an CPU DAI controller + spdif-controller: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + description: The phandle of an S/PDIF CPU DAI controller. + + spdif-out: + type: boolean + deprecated: true + description: | + If present, the transmitting function of S/PDIF will be enabled, + indicating there's a physical S/PDIF out connector or jack on the + board or it's connecting to some other IP block, such as an HDMI + encoder or display-controller. + + spdif-in: + type: boolean + deprecated: true + description: | + If present, the receiving function of S/PDIF will be enabled, + indicating there is a physical S/PDIF in connector/jack on the board. + required: - compatible - model @@ -195,3 +231,12 @@ examples: "AIN2L", "Line In Jack", "AIN2R", "Line In Jack"; }; + + - | + sound-spdif-asrc { + compatible = "fsl,imx-audio-spdif"; + model = "spdif-asrc-audio"; + audio-cpu = <&spdif>; + audio-asrc = <&easrc>; + audio-codec = <&spdifdit>, <&spdifdir>; + }; diff --git a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml b/Documentation/devicetree/bindings/sound/linux,spdif.yaml index fe5f0756af2f..0f4893e11ec4 100644 --- a/Documentation/devicetree/bindings/sound/linux,spdif-dit.yaml +++ b/Documentation/devicetree/bindings/sound/linux,spdif.yaml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/linux,spdif-dit.yaml# +$id: http://devicetree.org/schemas/sound/linux,spdif.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Dummy SPDIF Transmitter +title: Dummy SPDIF Transmitter/Receiver maintainers: - Mark Brown <broonie@kernel.org> @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: linux,spdif-dit + enum: + - linux,spdif-dit + - linux,spdif-dir "#sound-dai-cells": const: 0 diff --git a/Documentation/devicetree/bindings/sound/maxim,max98088.txt b/Documentation/devicetree/bindings/sound/maxim,max98088.txt deleted file mode 100644 index da764d913319..000000000000 --- a/Documentation/devicetree/bindings/sound/maxim,max98088.txt +++ /dev/null @@ -1,23 +0,0 @@ -MAX98088 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible: "maxim,max98088" or "maxim,max98089". -- reg: The I2C address of the device. - -Optional properties: - -- clocks: the clock provider of MCLK, see ../clock/clock-bindings.txt section - "consumer" for more information. -- clock-names: must be set to "mclk" - -Example: - -max98089: codec@10 { - compatible = "maxim,max98089"; - reg = <0x10>; - clocks = <&clks IMX6QDL_CLK_CKO2>; - clock-names = "mclk"; -}; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98088.yaml b/Documentation/devicetree/bindings/sound/maxim,max98088.yaml new file mode 100644 index 000000000000..e4a2967e1e81 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/maxim,max98088.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/maxim,max98088.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAX98088 audio CODEC + +maintainers: + - Abdulrasaq Lawani <abdulrasaqolawani@gmail.com> + +properties: + compatible: + enum: + - maxim,max98088 + - maxim,max98089 + + reg: + maxItems: 1 + + clocks: + items: + - description: master clock + + clock-names: + items: + - const: mclk + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + audio-codec@10 { + compatible = "maxim,max98089"; + reg = <0x10>; + clocks = <&clks 0>; + clock-names = "mclk"; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/zl38060.yaml b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml index 8bd201e573aa..994313fd12b2 100644 --- a/Documentation/devicetree/bindings/sound/zl38060.yaml +++ b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/zl38060.yaml# +$id: http://devicetree.org/schemas/sound/mscc,zl38060.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: ZL38060 Connected Home Audio Processor from Microsemi. diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml index 3dbf438c3841..232dc16a94a3 100644 --- a/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8824.yaml @@ -23,6 +23,14 @@ properties: '#sound-dai-cells': const: 0 + clocks: + items: + - description: The phandle of the master clock to the CODEC + + clock-names: + items: + - const: mclk + interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml new file mode 100644 index 000000000000..40a0877a8aba --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nxp,lpc3220-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32XX I2S Controller + +description: + The I2S controller in LPC32XX SoCs, ASoC DAI. + +maintainers: + - J.M.B. Downing <jonathan.downing@nautel.com> + - Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - nxp,lpc3220-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: input clock of the peripheral. + + dmas: + items: + - description: RX DMA Channel + - description: TX DMA Channel + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - interrupts + - clocks + - dmas + - dma-names + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/lpc32xx-clock.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2s@20094000 { + compatible = "nxp,lpc3220-i2s"; + reg = <0x20094000 0x1000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_I2S0>; + dmas = <&dma 0 1>, <&dma 13 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt b/Documentation/devicetree/bindings/sound/omap-mcpdm.txt deleted file mode 100644 index ff98a0cb5b3f..000000000000 --- a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Texas Instruments OMAP4+ McPDM - -Required properties: -- compatible: "ti,omap4-mcpdm" -- reg: Register location and size as an array: - <MPU access base address, size>, - <L3 interconnect address, size>; -- interrupts: Interrupt number for McPDM -- ti,hwmods: Name of the hwmod associated to the McPDM -- clocks: phandle for the pdmclk provider, likely <&twl6040> -- clock-names: Must be "pdmclk" - -Example: - -mcpdm: mcpdm@40132000 { - compatible = "ti,omap4-mcpdm"; - reg = <0x40132000 0x7f>, /* MPU private access */ - <0x49032000 0x7f>; /* L3 Interconnect */ - interrupts = <0 112 0x4>; - interrupt-parent = <&gic>; - ti,hwmods = "mcpdm"; -}; - -In board DTS file the pdmclk needs to be added: - -&mcpdm { - clocks = <&twl6040>; - clock-names = "pdmclk"; - status = "okay"; -}; diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 77006a4aec4a..47878a6df608 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -6,7 +6,7 @@ on the board). The TAS575x devices only support I2C. Required properties: - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141", - "ti,pcm5142", "ti,tas5754" or "ti,tas5756" + "ti,pcm5142", "ti,pcm5242", "ti,tas5754" or "ti,tas5756" - reg : the I2C address of the device for I2C, the chip select number for SPI. diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt deleted file mode 100644 index e1b9fa8a5bf8..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt +++ /dev/null @@ -1,128 +0,0 @@ -* Qualcomm Technologies APQ8096 ASoC sound card driver - -This binding describes the APQ8096 sound card, which uses qdsp for audio. - -- compatible: - Usage: required - Value type: <stringlist> - Definition: must be "qcom,apq8096-sndcard" - -- audio-routing: - Usage: Optional - Value type: <stringlist> - Definition: A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names could be power supplies, MicBias - of codec and the jacks on the board: - Valid names include: - - Board Connectors: - "Headphone Left" - "Headphone Right" - "Earphone" - "Line Out1" - "Line Out2" - "Line Out3" - "Line Out4" - "Analog Mic1" - "Analog Mic2" - "Analog Mic3" - "Analog Mic4" - "Analog Mic5" - "Analog Mic6" - "Digital Mic2" - "Digital Mic3" - - Audio pins and MicBias on WCD9335 Codec: - "MIC_BIAS1" - "MIC_BIAS2" - "MIC_BIAS3" - "MIC_BIAS4" - "AMIC1" - "AMIC2" - "AMIC3" - "AMIC4" - "AMIC5" - "AMIC6" - "AMIC6" - "DMIC1" - "DMIC2" - "DMIC3" - -- model: - Usage: required - Value type: <stringlist> - Definition: The user-visible name of this sound card. - -- aux-devs - Usage: optional - Value type: <array of phandles> - Definition: A list of phandles for auxiliary devices (e.g. analog - amplifiers) that do not appear directly within the DAI - links. Should be connected to another audio component - using "audio-routing". - -= dailinks -Each subnode of sndcard represents either a dailink, and subnodes of each -dailinks would be cpu/codec/platform dais. - -- link-name: - Usage: required - Value type: <string> - Definition: User friendly name for dai link - -= CPU, PLATFORM, CODEC dais subnodes -- cpu: - Usage: required - Value type: <subnode> - Definition: cpu dai sub-node - -- codec: - Usage: Optional - Value type: <subnode> - Definition: codec dai sub-node - -- platform: - Usage: Optional - Value type: <subnode> - Definition: platform dai sub-node - -- sound-dai: - Usage: required - Value type: <phandle with arguments> - Definition: dai phandle/s and port of CPU/CODEC/PLATFORM node. - -Obsolete: - qcom,model: String for soundcard name (Use model instead) - qcom,audio-routing: A list of the connections between audio components. - (Use audio-routing instead) - -Example: - -audio { - compatible = "qcom,apq8096-sndcard"; - model = "DB820c"; - - mm1-dai-link { - link-name = "MultiMedia1"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; - }; - }; - - hdmi-dai-link { - link-name = "HDMI Playback"; - cpu { - sound-dai = <&q6afe HDMI_RX>; - }; - - platform { - sound-dai = <&q6adm>; - }; - - codec { - sound-dai = <&hdmi 0>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml new file mode 100644 index 000000000000..a899c4e7c1c9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital-codec.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,msm8916-wcd-digital-codec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8916 WCD Digital Audio Codec + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: + The digital WCD audio codec found on Qualcomm MSM8916 LPASS. + +properties: + compatible: + const: qcom,msm8916-wcd-digital-codec + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: ahbix-clk + - const: mclk + + '#sound-dai-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#sound-dai-cells' + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-msm8916.h> + audio-codec@771c000 { + compatible = "qcom,msm8916-wcd-digital-codec"; + reg = <0x0771c000 0x400>; + clocks = <&gcc GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK>, + <&gcc GCC_CODEC_DIGCODEC_CLK>; + clock-names = "ahbix-clk", "mclk"; + #sound-dai-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt deleted file mode 100644 index 1c8e4cb25176..000000000000 --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-digital.txt +++ /dev/null @@ -1,20 +0,0 @@ -msm8916 digital audio CODEC - -## Bindings for codec core in lpass: - -Required properties - - compatible = "qcom,msm8916-wcd-digital-codec"; - - reg: address space for lpass codec. - - clocks: Handle to mclk and ahbclk - - clock-names: should be "mclk", "ahbix-clk". - -Example: - -audio-codec@771c000{ - compatible = "qcom,msm8916-wcd-digital-codec"; - reg = <0x0771c000 0x400>; - clocks = <&gcc GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK>, - <&gcc GCC_CODEC_DIGCODEC_CLK>; - clock-names = "ahbix-clk", "mclk"; - #sound-dai-cells = <1>; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index b2e15ebbd1bc..c9076dcd44c1 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -28,6 +28,7 @@ properties: - const: qcom,sm8450-sndcard - enum: - qcom,apq8016-sbc-sndcard + - qcom,apq8096-sndcard - qcom,msm8916-qdsp6-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs6490-rb3gen2-sndcard diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml new file mode 100644 index 000000000000..d3cf8f59cb23 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd937x-sdw.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SoundWire Slave devices on WCD9370/WCD9375 + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: | + Qualcomm WCD9370/WCD9375 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire slave devices. This bindings is for the + slave devices. + +properties: + compatible: + const: sdw20217010a00 + + 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 which are adc1_port, adc23_port, + dmic03_mbhc_port, dmic46_port. + Supports maximum 4 tx soundwire ports. + + WCD9370 TX Port 1 (ADC1) <=> SWR2 Port 2 + WCD9370 TX Port 2 (ADC2, 3) <=> SWR2 Port 2 + WCD9370 TX Port 3 (DMIC0,1,2,3 & MBHC) <=> SWR2 Port 3 + WCD9370 TX Port 4 (DMIC4,5,6,7) <=> SWR2 Port 4 + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + items: + enum: [1, 2, 3, 4] + + qcom,rx-port-mapping: + description: | + Specifies static port mapping between device and host rx ports. + In the order of device port index which are hph_port, clsh_port, + comp_port, lo_port, dsd port. + Supports maximum 5 rx soundwire ports. + + WCD9370 RX Port 1 (HPH_L/R) <==> SWR1 Port 1 (HPH_L/R) + WCD9370 RX Port 2 (CLSH) <==> SWR1 Port 2 (CLSH) + WCD9370 RX Port 3 (COMP_L/R) <==> SWR1 Port 3 (COMP_L/R) + WCD9370 RX Port 4 (LO) <==> SWR1 Port 4 (LO) + WCD9370 RX Port 5 (DSD_L/R) <==> SWR1 Port 5 (DSD) + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 5 + maxItems: 5 + items: + enum: [1, 2, 3, 4, 5] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soundwire@3210000 { + reg = <0x03210000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_rx: codec@0,4 { + compatible = "sdw20217010a00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; + }; + + soundwire@3230000 { + reg = <0x03230000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_tx: codec@0,3 { + compatible = "sdw20217010a00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 2 3 4>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml new file mode 100644 index 000000000000..de397d879acc --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,wcd937x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCD9370/WCD9375 Audio Codec + +maintainers: + - Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +description: + Qualcomm WCD9370/WCD9375 Codec is a standalone Hi-Fi audio codec IC. + It has RX and TX Soundwire slave devices. + +allOf: + - $ref: dai-common.yaml# + - $ref: qcom,wcd93xx-common.yaml# + +properties: + compatible: + oneOf: + - const: qcom,wcd9370-codec + - items: + - const: qcom,wcd9375-codec + - const: qcom,wcd9370-codec + + vdd-px-supply: + description: A reference to the 1.8V I/O supply + +required: + - compatible + - vdd-px-supply + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + codec { + compatible = "qcom,wcd9370-codec"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&wcd_reset_n>; + pinctrl-1 = <&wcd_reset_n_sleep>; + reset-gpios = <&tlmm 83 GPIO_ACTIVE_HIGH>; + vdd-buck-supply = <&vreg_l17b_1p8>; + vdd-rxtx-supply = <&vreg_l18b_1p8>; + vdd-px-supply = <&vreg_l18b_1p8>; + vdd-mic-bias-supply = <&vreg_bob>; + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,rx-device = <&wcd937x_rx>; + qcom,tx-device = <&wcd937x_tx>; + #sound-dai-cells = <1>; + }; + + /* ... */ + + soundwire@3210000 { + reg = <0x03210000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_rx: codec@0,4 { + compatible = "sdw20217010a00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; + }; + + soundwire@3230000 { + reg = <0x03230000 0x2000>; + #address-cells = <2>; + #size-cells = <0>; + wcd937x_tx: codec@0,3 { + compatible = "sdw20217010a00"; + reg = <0 3>; + qcom,tx-port-mapping = <1 2 3 4>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml index 8e462cdf0018..14d312f9c345 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml @@ -32,6 +32,14 @@ properties: vdd-supply: description: VDD Supply for the Codec + qcom,port-mapping: + description: | + Specifies static port mapping between slave and master ports. + In the order of slave port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 4 + maxItems: 4 + '#thermal-sensor-cells': const: 0 diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml index 22798d22d981..83e0360301e1 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml @@ -32,6 +32,14 @@ properties: description: Powerdown/Shutdown line to use (pin SD_N) maxItems: 1 + qcom,port-mapping: + description: | + Specifies static port mapping between slave and master ports. + In the order of slave port index. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 6 + maxItems: 6 + '#sound-dai-cells': const: 0 diff --git a/Documentation/devicetree/bindings/sound/rt1019.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml index 3d5a91a942f4..adf5e38f4dbc 100644 --- a/Documentation/devicetree/bindings/sound/rt1019.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/rt1019.yaml# +$id: http://devicetree.org/schemas/sound/realtek,rt1019.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: RT1019 Mono Class-D Audio Amplifier diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml new file mode 100644 index 000000000000..7fbf7739c371 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5514.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5514.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5514 audio CODEC + +maintainers: + - Animesh Agarwal <animeshagarwal28@gmail.com> + +description: | + This device supports both I2C and SPI. + + Pins on the device (for linking into audio routes) for I2C: + * DMIC1L + * DMIC1R + * DMIC2L + * DMIC2R + * AMICL + * AMICR + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - $ref: dai-common.yaml# + +properties: + compatible: + const: realtek,rt5514 + + reg: + maxItems: 1 + + clocks: + items: + - description: Master clock to the CODEC + + clock-names: + items: + - const: mclk + + interrupts: + maxItems: 1 + description: The interrupt number to the cpu. + + realtek,dmic-init-delay-ms: + description: Set the DMIC initial delay (ms) to wait it ready for I2C. + + spi-max-frequency: true + + wakeup-source: + type: boolean + description: Flag to indicate this device can wake system (suspend/resume). + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@57 { + compatible = "realtek,rt5514"; + reg = <0x57>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml new file mode 100644 index 000000000000..747a731c44c9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5631.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5631.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ALC5631/RT5631 audio CODEC + +maintainers: + - Animesh Agarwal <animeshagarwal28@gmail.com> + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes): + * SPK_OUT_R_P + * SPK_OUT_R_N + * SPK_OUT_L_P + * SPK_OUT_L_N + * HP_OUT_L + * HP_OUT_R + * AUX_OUT2_LP + * AUX_OUT2_RN + * AUX_OUT1_LP + * AUX_OUT1_RN + * AUX_IN_L_JD + * AUX_IN_R_JD + * MONO_IN_P + * MONO_IN_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MONO_OUT_P + * MONO_OUT_N + * MICBIAS1 + * MICBIAS2 + +properties: + compatible: + enum: + - realtek,alc5631 + - realtek,rt5631 + + reg: + maxItems: 1 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@1a { + compatible = "realtek,alc5631"; + reg = <0x1a>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml new file mode 100644 index 000000000000..13f09f1bc800 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5645.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5645.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5650/RT5645 audio CODEC + +maintainers: + - Animesh Agarwal <animeshagarwal28@gmail.com> + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes) for RT5645/RT5650: + * DMIC L1 + * DMIC R1 + * DMIC L2 + * DMIC R2 + * IN1P + * IN1N + * IN2P + * IN2N + * Haptic Generator + * HPOL + * HPOR + * LOUTL + * LOUTR + * PDM1L + * PDM1R + * SPOL + * SPOR + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - realtek,rt5645 + - realtek,rt5650 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: The CODEC's interrupt output. + + avdd-supply: + description: Power supply for AVDD, providing 1.8V. + + cpvdd-supply: + description: Power supply for CPVDD, providing 3.5V. + + hp-detect-gpios: + description: + A GPIO spec for the external headphone detect pin. If jd-mode = 0, we + will get the JD status by getting the value of hp-detect-gpios. + maxItems: 1 + + cbj-sleeve-gpios: + description: + A GPIO spec to control the external combo jack circuit to tie the + sleeve/ring2 contacts to the ground or floating. It could avoid some + electric noise from the active speaker jacks. + maxItems: 1 + + realtek,in2-differential: + description: + Indicate MIC2 input are differential, rather than single-ended. + type: boolean + + realtek,dmic1-data-pin: + description: Specify which pin to be used as DMIC1 data pin. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic1 is not used + - 1 # using IN2P pin as dmic1 data pin + - 2 # using GPIO6 pin as dmic1 data pin + - 3 # using GPIO10 pin as dmic1 data pin + - 4 # using GPIO12 pin as dmic1 data pin + + realtek,dmic2-data-pin: + description: Specify which pin to be used as DMIC2 data pin. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic2 is not used + - 1 # using IN2N pin as dmic2 data pin + - 2 # using GPIO5 pin as dmic2 data pin + - 3 # using GPIO11 pin as dmic2 data pin + + realtek,jd-mode: + description: The JD mode of rt5645/rt5650. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # rt5645/rt5650 JD function is not used + - 1 # Mode-0 (VDD=3.3V), two port jack detection + - 2 # Mode-1 (VDD=3.3V), one port jack detection + - 3 # Mode-2 (VDD=1.8V), one port jack detection + +required: + - compatible + - reg + - interrupts + - avdd-supply + - cpvdd-supply + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "realtek,rt5650"; + reg = <0x1a>; + hp-detect-gpios = <&gpio 19 0>; + cbj-sleeve-gpios = <&gpio 20 0>; + interrupt-parent = <&gpio>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + avdd-supply = <&avdd_reg>; + cpvdd-supply = <&cpvdd_supply>; + realtek,jd-mode = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml new file mode 100644 index 000000000000..1100ffd9a7c0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5659.yaml @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5659.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5659/RT5658 audio CODEC + +maintainers: + - Animesh Agarwal <animeshagarwal28@gmail.com> + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes) for RT5659/RT5658: + * DMIC L1 + * DMIC R1 + * DMIC L2 + * DMIC R2 + * IN1P + * IN1N + * IN2P + * IN2N + * IN3P + * IN3N + * IN4P + * IN4N + * HPOL + * HPOR + * SPOL + * SPOR + * LOUTL + * LOUTR + * MONOOUT + * PDML + * PDMR + * SPDIF + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - realtek,rt5659 + - realtek,rt5658 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: mclk + + realtek,dmic1-data-pin: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic1 is not used + - 1 # using IN2N pin as dmic1 data pin + - 2 # using GPIO5 pin as dmic1 data pin + - 3 # using GPIO9 pin as dmic1 data pin + - 4 # using GPIO11 pin as dmic1 data pin + description: Specify which pin to be used as DMIC1 data pin. + default: 0 + + realtek,dmic2-data-pin: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # dmic2 is not used + - 1 # using IN2P pin as dmic2 data pin + - 2 # using GPIO6 pin as dmic2 data pin + - 3 # using GPIO10 pin as dmic2 data pin + - 4 # using GPIO12 pin as dmic2 data pin + description: Specify which pin to be used as DMIC2 data pin. + default: 0 + + realtek,jd-src: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # No JD is used + - 1 # using JD3 as JD source + - 2 # JD source for Intel HDA header + description: Specify which JD source be used. + default: 0 + + realtek,ldo1-en-gpios: + maxItems: 1 + description: CODEC's LDO1_EN pin. + + realtek,reset-gpios: + maxItems: 1 + description: CODEC's RESET pin. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1b { + compatible = "realtek,rt5659"; + reg = <0x1b>; + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + realtek,ldo1-en-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml new file mode 100644 index 000000000000..9ce23e58e5ea --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,rt5677.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RT5677 audio CODEC + +maintainers: + - Animesh Agarwal <animeshagarwal28@gmail.com> + +description: | + This device supports I2C only. + + Pins on the device (for linking into audio routes): + * IN1P + * IN1N + * IN2P + * IN2N + * MICBIAS1 + * DMIC1 + * DMIC2 + * DMIC3 + * DMIC4 + * LOUT1 + * LOUT2 + * LOUT3 + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: realtek,rt5677 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + realtek,pow-ldo2-gpio: + maxItems: 1 + description: CODEC's POW_LDO2 pin. + + realtek,reset-gpio: + maxItems: 1 + description: CODEC's RESET pin. Active low. + + realtek,gpio-config: + description: | + Array of six 8bit elements that configures GPIO. + 0 - floating (reset value) + 1 - pull down + 2 - pull up + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 6 + maxItems: 6 + items: + maximum: 2 + + realtek,jd1-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO1 for jd1. + - 2 # GPIO2 for jd1. + - 3 # GPIO3 for jd1. + description: Configures GPIO Mic Jack detection 1. + + realtek,jd2-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO4 for jd2. + - 2 # GPIO5 for jd2. + - 3 # GPIO6 for jd2. + description: Configures GPIO Mic Jack detection 2. + + realtek,jd3-gpio: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 0 # OFF + - 1 # GPIO4 for jd3. + - 2 # GPIO5 for jd3. + - 3 # GPIO6 for jd3. + description: Configures GPIO Mic Jack detection 3. + +patternProperties: + '^realtek,in[1-2]-differential$': + type: boolean + description: Indicate MIC1/2 input are differential, rather than + single-ended. + + '^realtek,lout[1-3]-differential$': + type: boolean + description: Indicate LOUT1/2/3 outputs are differential, rather than + single-ended. + +required: + - compatible + - reg + - interrupts + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@2c { + compatible = "realtek,rt5677"; + reg = <0x2c>; + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + realtek,pow-ldo2-gpio = <&gpio 3 GPIO_ACTIVE_HIGH>; + realtek,reset-gpio = <&gpio 3 GPIO_ACTIVE_LOW>; + realtek,in1-differential; + realtek,gpio-config = <0 0 0 0 0 2>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt deleted file mode 100644 index d2cc171f22f2..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5514.txt +++ /dev/null @@ -1,37 +0,0 @@ -RT5514 audio CODEC - -This device supports both I2C and SPI. - -Required properties: - -- compatible : "realtek,rt5514". - -- reg : the I2C address of the device for I2C, the chip select - number for SPI. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC -- clock-names: Should be "mclk" - -- interrupts: The interrupt number to the cpu. The interrupt specifier format - depends on the interrupt controller. - -- realtek,dmic-init-delay-ms - Set the DMIC initial delay (ms) to wait it ready for I2C. - -Pins on the device (for linking into audio routes) for I2C: - - * DMIC1L - * DMIC1R - * DMIC2L - * DMIC2R - * AMICL - * AMICR - -Example: - -rt5514: codec@57 { - compatible = "realtek,rt5514"; - reg = <0x57>; -}; diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt deleted file mode 100644 index 56bc85232c49..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5631.txt +++ /dev/null @@ -1,48 +0,0 @@ -ALC5631/RT5631 audio CODEC - -This device supports I2C only. - -Required properties: - - - compatible : "realtek,alc5631" or "realtek,rt5631" - - - reg : the I2C address of the device. - -Pins on the device (for linking into audio routes): - - * SPK_OUT_R_P - * SPK_OUT_R_N - * SPK_OUT_L_P - * SPK_OUT_L_N - * HP_OUT_L - * HP_OUT_R - * AUX_OUT2_LP - * AUX_OUT2_RN - * AUX_OUT1_LP - * AUX_OUT1_RN - * AUX_IN_L_JD - * AUX_IN_R_JD - * MONO_IN_P - * MONO_IN_N - * MIC1_P - * MIC1_N - * MIC2_P - * MIC2_N - * MONO_OUT_P - * MONO_OUT_N - * MICBIAS1 - * MICBIAS2 - -Example: - -alc5631: audio-codec@1a { - compatible = "realtek,alc5631"; - reg = <0x1a>; -}; - -or - -rt5631: audio-codec@1a { - compatible = "realtek,rt5631"; - reg = <0x1a>; -}; diff --git a/Documentation/devicetree/bindings/sound/rt5645.txt b/Documentation/devicetree/bindings/sound/rt5645.txt deleted file mode 100644 index c1fa379f5f3e..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5645.txt +++ /dev/null @@ -1,82 +0,0 @@ -RT5650/RT5645 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : One of "realtek,rt5645" or "realtek,rt5650". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -- avdd-supply: Power supply for AVDD, providing 1.8V. - -- cpvdd-supply: Power supply for CPVDD, providing 3.5V. - -Optional properties: - -- hp-detect-gpios: - a GPIO spec for the external headphone detect pin. If jd-mode = 0, - we will get the JD status by getting the value of hp-detect-gpios. - -- cbj-sleeve-gpios: - a GPIO spec to control the external combo jack circuit to tie the sleeve/ring2 - contacts to the ground or floating. It could avoid some electric noise from the - active speaker jacks. - -- realtek,in2-differential - Boolean. Indicate MIC2 input are differential, rather than single-ended. - -- realtek,dmic1-data-pin - 0: dmic1 is not used - 1: using IN2P pin as dmic1 data pin - 2: using GPIO6 pin as dmic1 data pin - 3: using GPIO10 pin as dmic1 data pin - 4: using GPIO12 pin as dmic1 data pin - -- realtek,dmic2-data-pin - 0: dmic2 is not used - 1: using IN2N pin as dmic2 data pin - 2: using GPIO5 pin as dmic2 data pin - 3: using GPIO11 pin as dmic2 data pin - --- realtek,jd-mode : The JD mode of rt5645/rt5650 - 0 : rt5645/rt5650 JD function is not used - 1 : Mode-0 (VDD=3.3V), two port jack detection - 2 : Mode-1 (VDD=3.3V), one port jack detection - 3 : Mode-2 (VDD=1.8V), one port jack detection - -Pins on the device (for linking into audio routes) for RT5645/RT5650: - - * DMIC L1 - * DMIC R1 - * DMIC L2 - * DMIC R2 - * IN1P - * IN1N - * IN2P - * IN2N - * Haptic Generator - * HPOL - * HPOR - * LOUTL - * LOUTR - * PDM1L - * PDM1R - * SPOL - * SPOR - -Example: - -codec: rt5650@1a { - compatible = "realtek,rt5650"; - reg = <0x1a>; - hp-detect-gpios = <&gpio 19 0>; - cbj-sleeve-gpios = <&gpio 20 0>; - interrupt-parent = <&gpio>; - interrupts = <7 IRQ_TYPE_EDGE_FALLING>; - realtek,dmic-en = "true"; - realtek,en-jd-func = "true"; - realtek,jd-mode = <3>; -}; diff --git a/Documentation/devicetree/bindings/sound/rt5659.txt b/Documentation/devicetree/bindings/sound/rt5659.txt deleted file mode 100644 index 8f3f62c0226a..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5659.txt +++ /dev/null @@ -1,89 +0,0 @@ -RT5659/RT5658 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : One of "realtek,rt5659" or "realtek,rt5658". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -Optional properties: - -- clocks: The phandle of the master clock to the CODEC -- clock-names: Should be "mclk" - -- realtek,in1-differential -- realtek,in3-differential -- realtek,in4-differential - Boolean. Indicate MIC1/3/4 input are differential, rather than single-ended. - -- realtek,dmic1-data-pin - 0: dmic1 is not used - 1: using IN2N pin as dmic1 data pin - 2: using GPIO5 pin as dmic1 data pin - 3: using GPIO9 pin as dmic1 data pin - 4: using GPIO11 pin as dmic1 data pin - -- realtek,dmic2-data-pin - 0: dmic2 is not used - 1: using IN2P pin as dmic2 data pin - 2: using GPIO6 pin as dmic2 data pin - 3: using GPIO10 pin as dmic2 data pin - 4: using GPIO12 pin as dmic2 data pin - -- realtek,jd-src - 0: No JD is used - 1: using JD3 as JD source - 2: JD source for Intel HDA header - -- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. -- realtek,reset-gpios : The GPIO that controls the CODEC's RESET pin. - -- sound-name-prefix: Please refer to dai-common.yaml - -- ports: A Codec may have a single or multiple I2S interfaces. These - interfaces on Codec side can be described under 'ports' or 'port'. - When the SoC or host device is connected to multiple interfaces of - the Codec, the connectivity can be described using 'ports' property. - If a single interface is used, then 'port' can be used. The usage - depends on the platform or board design. - Please refer to Documentation/devicetree/bindings/graph.txt - -Pins on the device (for linking into audio routes) for RT5659/RT5658: - - * DMIC L1 - * DMIC R1 - * DMIC L2 - * DMIC R2 - * IN1P - * IN1N - * IN2P - * IN2N - * IN3P - * IN3N - * IN4P - * IN4N - * HPOL - * HPOR - * SPOL - * SPOR - * LOUTL - * LOUTR - * MONOOUT - * PDML - * PDMR - * SPDIF - -Example: - -rt5659 { - compatible = "realtek,rt5659"; - reg = <0x1b>; - interrupt-parent = <&gpio>; - interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>; - realtek,ldo1-en-gpios = - <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; -}; diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt deleted file mode 100644 index da2430099181..000000000000 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ /dev/null @@ -1,78 +0,0 @@ -RT5677 audio CODEC - -This device supports I2C only. - -Required properties: - -- compatible : "realtek,rt5677". - -- reg : The I2C address of the device. - -- interrupts : The CODEC's interrupt output. - -- gpio-controller : Indicates this device is a GPIO controller. - -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters (currently unused). - -Optional properties: - -- realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin. -- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. Active low. - -- realtek,in1-differential -- realtek,in2-differential -- realtek,lout1-differential -- realtek,lout2-differential -- realtek,lout3-differential - Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential, - rather than single-ended. - -- realtek,gpio-config - Array of six 8bit elements that configures GPIO. - 0 - floating (reset value) - 1 - pull down - 2 - pull up - -- realtek,jd1-gpio - Configures GPIO Mic Jack detection 1. - Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively. - -- realtek,jd2-gpio -- realtek,jd3-gpio - Configures GPIO Mic Jack detection 2 and 3. - Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively. - -Pins on the device (for linking into audio routes): - - * IN1P - * IN1N - * IN2P - * IN2N - * MICBIAS1 - * DMIC1 - * DMIC2 - * DMIC3 - * DMIC4 - * LOUT1 - * LOUT2 - * LOUT3 - -Example: - -rt5677 { - compatible = "realtek,rt5677"; - reg = <0x2c>; - interrupt-parent = <&gpio>; - interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>; - - gpio-controller; - #gpio-cells = <2>; - - realtek,pow-ldo2-gpio = - <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; - realtek,reset-gpio = <&gpio TEGRA_GPIO(BB, 3) GPIO_ACTIVE_LOW>; - realtek,in1-differential = "true"; - realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */ - realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */ -}; diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml index 6ec80f529d84..69ddfd4afdcd 100644 --- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml @@ -53,6 +53,9 @@ properties: submic-bias-supply: description: Supply for the micbias on the Sub microphone + headset-mic-bias-supply: + description: Supply for the micbias on the Headset microphone + fm-sel-gpios: maxItems: 1 description: GPIO pin for FM selection @@ -61,6 +64,36 @@ properties: maxItems: 1 description: GPIO pin for line out selection + headset-detect-gpios: + maxItems: 1 + description: GPIO for detection of headset insertion + + headset-key-gpios: + maxItems: 1 + description: GPIO for detection of headset key press + + io-channels: + maxItems: 1 + description: IO channel to read micbias voltage for headset detection + + io-channel-names: + const: headset-detect + + samsung,headset-4pole-threshold-microvolt: + minItems: 2 + maxItems: 2 + description: + Array containing minimum and maximum IO channel value for 4-pole + (with microphone/button) headsets. If the IO channel value is + outside of this range, a 3-pole headset is assumed. + + samsung,headset-button-threshold-microvolt: + minItems: 3 + maxItems: 3 + description: | + Array of minimum (inclusive) IO channel values for headset button + detection, in order: "Media", "Volume Up" and "Volume Down". + required: - compatible - cpu diff --git a/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml b/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml index 9f319caf3db7..194ac1d4f4f5 100644 --- a/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml +++ b/Documentation/devicetree/bindings/sound/simple-audio-mux.yaml @@ -24,6 +24,11 @@ properties: description: | GPIOs used to select the input line. + state-labels: + description: State of input line. default is "Input 1", "Input 2" + $ref: /schemas/types.yaml#/definitions/string-array + maxItems: 2 + sound-name-prefix: true required: @@ -37,4 +42,5 @@ examples: mux { compatible = "simple-audio-mux"; mux-gpios = <&gpio 3 0>; + state-labels = "Label_A", "Label_B"; }; diff --git a/Documentation/devicetree/bindings/sound/spdif-receiver.txt b/Documentation/devicetree/bindings/sound/spdif-receiver.txt deleted file mode 100644 index 80f807bf8a1d..000000000000 --- a/Documentation/devicetree/bindings/sound/spdif-receiver.txt +++ /dev/null @@ -1,10 +0,0 @@ -Device-Tree bindings for dummy spdif receiver - -Required properties: - - compatible: should be "linux,spdif-dir". - -Example node: - - codec: spdif-receiver { - compatible = "linux,spdif-dir"; - }; diff --git a/Documentation/devicetree/bindings/sound/tas571x.txt b/Documentation/devicetree/bindings/sound/tas571x.txt deleted file mode 100644 index 1addc75989d5..000000000000 --- a/Documentation/devicetree/bindings/sound/tas571x.txt +++ /dev/null @@ -1,49 +0,0 @@ -Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers - -The codec is controlled through an I2C interface. It also has two other -signals that can be wired up to GPIOs: reset (strongly recommended), and -powerdown (optional). - -Required properties: - -- compatible: should be one of the following: - - "ti,tas5707" - - "ti,tas5711", - - "ti,tas5717", - - "ti,tas5719", - - "ti,tas5721" - - "ti,tas5733" -- reg: The I2C address of the device -- #sound-dai-cells: must be equal to 0 - -Optional properties: - -- reset-gpios: GPIO specifier for the TAS571x's active low reset line -- pdn-gpios: GPIO specifier for the TAS571x's active low powerdown line -- clocks: clock phandle for the MCLK input -- clock-names: should be "mclk" -- AVDD-supply: regulator phandle for the AVDD supply (all chips) -- DVDD-supply: regulator phandle for the DVDD supply (all chips) -- HPVDD-supply: regulator phandle for the HPVDD supply (5717/5719) -- PVDD_AB-supply: regulator phandle for the PVDD_AB supply (5717/5719) -- PVDD_CD-supply: regulator phandle for the PVDD_CD supply (5717/5719) -- PVDD_A-supply: regulator phandle for the PVDD_A supply (5711) -- PVDD_B-supply: regulator phandle for the PVDD_B supply (5711) -- PVDD_C-supply: regulator phandle for the PVDD_C supply (5711) -- PVDD_D-supply: regulator phandle for the PVDD_D supply (5711) -- DRVDD-supply: regulator phandle for the DRVDD supply (5721) -- PVDD-supply: regulator phandle for the PVDD supply (5721) - -Example: - - tas5717: audio-codec@2a { - compatible = "ti,tas5717"; - reg = <0x2a>; - #sound-dai-cells = <0>; - - reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; - pdn-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>; - - clocks = <&clk_core CLK_I2S>; - clock-names = "mclk"; - }; diff --git a/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml b/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml new file mode 100644 index 000000000000..cdea0a00826a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,omap4-mcpdm.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,omap4-mcpdm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OMAP McPDM + +maintainers: + - Misael Lopez Cruz <misael.lopez@ti.com> + +description: + OMAP ALSA SoC DAI driver using McPDM port used by TWL6040 + +properties: + compatible: + const: ti,omap4-mcpdm + + reg: + items: + - description: MPU access base address + - description: L3 interconnect address + + reg-names: + items: + - const: mpu + - const: dma + + interrupts: + maxItems: 1 + + dmas: + maxItems: 2 + + dma-names: + items: + - const: up_link + - const: dn_link + + clocks: + maxItems: 1 + + clock-names: + items: + - const: pdmclk + +required: + - compatible + - reg + - reg-names + - interrupts + - dmas + - dma-names + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + mcpdm@0 { + compatible = "ti,omap4-mcpdm"; + reg = <0x0 0x7f>, /* MPU private access */ + <0x49032000 0x7f>; /* L3 Interconnect */ + reg-names = "mpu", "dma"; + interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gic>; + dmas = <&sdma 65>, <&sdma 66>; + dma-names = "up_link", "dn_link"; + clocks = <&twl6040>; + clock-names = "pdmclk"; + }; diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml index d28c102c0ce7..8bc3b0c7531e 100644 --- a/Documentation/devicetree/bindings/sound/tas2562.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas2562.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas2562.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2562 Smart PA diff --git a/Documentation/devicetree/bindings/sound/tas2770.yaml b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml index be2536e8c440..362c2e6154f0 100644 --- a/Documentation/devicetree/bindings/sound/tas2770.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-20 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas2770.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas2770.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2770 Smart PA diff --git a/Documentation/devicetree/bindings/sound/tas27xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml index f2d878f6f495..530bc3937847 100644 --- a/Documentation/devicetree/bindings/sound/tas27xx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2020-2022 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas27xx.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas27xx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2764/TAS2780 Smart PA diff --git a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml new file mode 100644 index 000000000000..2f917238db95 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,tas57xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TAS5711/TAS5717/TAS5719/TAS5721 stereo power amplifiers + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +properties: + compatible: + enum: + - ti,tas5707 + - ti,tas5711 + - ti,tas5717 + - ti,tas5719 + - ti,tas5721 + - ti,tas5733 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: GPIO for the active low reset line + + pdn-gpios: + maxItems: 1 + description: GPIO for the active low powerdown line + + clocks: + maxItems: 1 + + clock-names: + const: mclk + + AVDD-supply: true + DVDD-supply: true + HPVDD-supply: true + PVDD_AB-supply: true + PVDD_CD-supply: true + PVDD_A-supply: true + PVDD_B-supply: true + PVDD_C-supply: true + PVDD_D-supply: true + DRVDD-supply: true + PVDD-supply: true + + '#sound-dai-cells': + const: 0 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - '#sound-dai-cells' + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - ti,tas5717 + - ti,tas5719 + then: + properties: + PVDD_A-supply: false + PVDD_B-supply: false + PVDD_C-supply: false + PVDD_D-supply: false + DRVDD-supply: false + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - ti,tas5711 + then: + properties: + HPVDD-supply: false + PVDD_AB-supply: false + PVDD_CD-supply: false + DRVDD-supply: false + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - ti,tas5721 + then: + properties: + HPVDD-supply: false + PVDD_AB-supply: false + PVDD_CD-supply: false + PVDD_A-supply: false + PVDD_B-supply: false + PVDD_C-supply: false + PVDD_D-supply: false + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@2a { + compatible = "ti,tas5717"; + reg = <0x2a>; + #sound-dai-cells = <0>; + reset-gpios = <&gpio1 15 0>; + pdn-gpios = <&gpio1 15 0>; + AVDD-supply = <&avdd_supply>; + DVDD-supply = <&dvdd_supply>; + HPVDD-supply = <&hpvdd_supply>; + PVDD_AB-supply = <&pvdd_ab_supply>; + PVDD_CD-supply = <&pvdd_cd_supply>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/tas5805m.yaml b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml index 12c41974274e..c2c2835a9e1d 100644 --- a/Documentation/devicetree/bindings/sound/tas5805m.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas5805m.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas5805m.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: TAS5805M audio amplifier diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml index ede14ca2c07a..66b76656229f 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -58,8 +58,8 @@ properties: description: | Configuration for DMDIN/GPIO1 pin. - When ADC3XXX_GPIO_GPO is configured, this causes corresponding the - ALSA control "GPIOx Output" to appear, as a switch control. + When ADC3XXX_GPIO_GPO is selected, the pin may be controlled via the + GPIO framework, as pin number 0 on the device. ti,dmclk-gpio2: $ref: /schemas/types.yaml#/definitions/uint32 @@ -76,12 +76,32 @@ properties: description: | Configuration for DMCLK/GPIO2 pin. - When ADC3XXX_GPIO_GPO is configured, this causes corresponding the - ALSA control "GPIOx Output" to appear, as a switch control. + When ADC3XXX_GPIO_GPO is selected, the pin may be controlled via the + GPIO framework, as pin number 1 on the device. Note that there is currently no support for reading the GPIO pins as inputs. + ti,micbias1-gpo: + type: boolean + description: | + When set, the MICBIAS1 pin may be controlled via the GPIO framework, + as pin number 3 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias1-vg property. When deactivated, the pin will + float. + + ti,micbias2-gpo: + type: boolean + description: | + When set, the MICBIAS2 pin may be controlled via the GPIO framework, + as pin number 4 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias2-vg property. When deactivated, the pin will + float. + ti,micbias1-vg: $ref: /schemas/types.yaml#/definitions/uint32 enum: @@ -104,6 +124,10 @@ properties: description: | Mic bias voltage output on MICBIAS2 pin +dependencies: + ti,micbias1-gpo: ['ti,micbias1-vg'] + ti,micbias2-gpo: ['ti,micbias2-vg'] + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml index f3274bcc4c05..876fa97bfbcd 100644 --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tlv320adcx140.yaml# +$id: http://devicetree.org/schemas/sound/ti,tlv320adcx140.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter diff --git a/Documentation/devicetree/bindings/sound/wm8750.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml index 24246ac7bbdf..96859e38315b 100644 --- a/Documentation/devicetree/bindings/sound/wm8750.yaml +++ b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/wm8750.yaml# +$id: http://devicetree.org/schemas/sound/wlf,wm8750.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: WM8750 and WM8987 audio CODECs diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml new file mode 100644 index 000000000000..d0bbdc9f9ced --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8782.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8782.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wolfson Microelectromics WM8782 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: wlf,wm8782 + + Vdda-supply: + description: Regulator for the analog power supply (2.7V - 5.5V) + + Vdd-supply: + description: Regulator for the digital power supply (2.7V - 3.6V) + + wlf,fsampen: + description: FSAMPEN pin value, 0 for low, 1 for high, 2 for disconnected. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - Vdda-supply + - Vdd-supply + +unevaluatedProperties: false + +examples: + - | + wm8782: codec { + compatible = "wlf,wm8782"; + Vdda-supply = <&vdda_supply>; + Vdd-supply = <&vdd_supply>; + wlf,fsampen = <2>; + }; diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml new file mode 100644 index 000000000000..3c060179f06e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8804.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8804.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WM8804 audio codec + +description: | + This device supports both I2C and SPI (configured with pin strapping on the + board). + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + const: wlf,wm8804 + + reg: + description: + The I2C address of the device for I2C, the chip select number for SPI. + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + PVDD-supply: + description: PLL core supply + + DVDD-supply: + description: Digital core supply + + wlf,reset-gpio: + description: A GPIO specifier for the GPIO controlling the reset pin. + maxItems: 1 + +required: + - reg + - compatible + - PVDD-supply + - DVDD-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "wlf,wm8804"; + reg = <0x1a>; + PVDD-supply = <&pvdd_reg>; + DVDD-supply = <&dvdd_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wm8782.txt b/Documentation/devicetree/bindings/sound/wm8782.txt deleted file mode 100644 index 1a28f3280972..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8782.txt +++ /dev/null @@ -1,24 +0,0 @@ -WM8782 stereo ADC - -This device does not have any control interface or reset pins. - -Required properties: - - - compatible : "wlf,wm8782" - - Vdda-supply : phandle to a regulator for the analog power supply (2.7V - 5.5V) - - Vdd-supply : phandle to a regulator for the digital power supply (2.7V - 3.6V) - -Optional properties: - - - wlf,fsampen: - FSAMPEN pin value, 0 for low, 1 for high, 2 for disconnected. - Defaults to 0 if left unspecified. - -Example: - -wm8782: stereo-adc { - compatible = "wlf,wm8782"; - Vdda-supply = <&vdda_supply>; - Vdd-supply = <&vdd_supply>; - wlf,fsampen = <2>; /* 192KHz */ -}; diff --git a/Documentation/devicetree/bindings/sound/wm8804.txt b/Documentation/devicetree/bindings/sound/wm8804.txt deleted file mode 100644 index 2c1641c17a91..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8804.txt +++ /dev/null @@ -1,25 +0,0 @@ -WM8804 audio CODEC - -This device supports both I2C and SPI (configured with pin strapping -on the board). - -Required properties: - - - compatible : "wlf,wm8804" - - - reg : the I2C address of the device for I2C, the chip select - number for SPI. - - - PVDD-supply, DVDD-supply : Power supplies for the device, as covered - in Documentation/devicetree/bindings/regulator/regulator.txt - -Optional properties: - - - wlf,reset-gpio: A GPIO specifier for the GPIO controlling the reset pin - -Example: - -wm8804: codec@1a { - compatible = "wlf,wm8804"; - reg = <0x1a>; -}; diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 555c2f839969..1fb3f5e6193c 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -150,6 +150,12 @@ applicable everywhere (see syntax). That will limit the usefulness but on the other hand avoid the illegal configurations all over. + If "select" <symbol> is followed by "if" <expr>, <symbol> will be + selected by the logical AND of the value of the current menu symbol + and <expr>. This means, the lower limit can be downgraded due to the + presence of "if" <expr>. This behavior may seem weird, but we rely on + it. (The future of this behavior is undecided.) + - weak reverse dependencies: "imply" <symbol> ["if" <expr>] This is similar to "select" as it enforces a lower limit on another @@ -184,7 +190,7 @@ applicable everywhere (see syntax). ability to hook into a secondary subsystem while allowing the user to configure that subsystem out without also having to unset these drivers. - Note: If the combination of FOO=y and BAR=m causes a link error, + Note: If the combination of FOO=y and BAZ=m causes a link error, you can guard the function call with IS_REACHABLE():: foo_init() @@ -202,6 +208,10 @@ applicable everywhere (see syntax). imply BAR imply BAZ + Note: If "imply" <symbol> is followed by "if" <expr>, the default of <symbol> + will be the logical AND of the value of the current menu symbol and <expr>. + (The future of this behavior is undecided.) + - limiting menu display: "visible if" <expr> This attribute is only applicable to menu blocks, if the condition is diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst index 72da7057e4cf..dceeb0d763aa 100644 --- a/Documentation/networking/af_xdp.rst +++ b/Documentation/networking/af_xdp.rst @@ -329,24 +329,23 @@ XDP_SHARED_UMEM option and provide the initial socket's fd in the sxdp_shared_umem_fd field as you registered the UMEM on that socket. These two sockets will now share one and the same UMEM. -In this case, it is possible to use the NIC's packet steering -capabilities to steer the packets to the right queue. This is not -possible in the previous example as there is only one queue shared -among sockets, so the NIC cannot do this steering as it can only steer -between queues. - -In libxdp (or libbpf prior to version 1.0), you need to use the -xsk_socket__create_shared() API as it takes a reference to a FILL ring -and a COMPLETION ring that will be created for you and bound to the -shared UMEM. You can use this function for all the sockets you create, -or you can use it for the second and following ones and use -xsk_socket__create() for the first one. Both methods yield the same -result. +There is no need to supply an XDP program like the one in the previous +case where sockets were bound to the same queue id and +device. Instead, use the NIC's packet steering capabilities to steer +the packets to the right queue. In the previous example, there is only +one queue shared among sockets, so the NIC cannot do this steering. It +can only steer between queues. + +In libbpf, you need to use the xsk_socket__create_shared() API as it +takes a reference to a FILL ring and a COMPLETION ring that will be +created for you and bound to the shared UMEM. You can use this +function for all the sockets you create, or you can use it for the +second and following ones and use xsk_socket__create() for the first +one. Both methods yield the same result. Note that a UMEM can be shared between sockets on the same queue id and device, as well as between queues on the same device and between -devices at the same time. It is also possible to redirect to any -socket as long as it is bound to the same umem with XDP_SHARED_UMEM. +devices at the same time. XDP_USE_NEED_WAKEUP bind flag ----------------------------- @@ -823,10 +822,6 @@ A: The short answer is no, that is not supported at the moment. The switch, or other distribution mechanism, in your NIC to direct traffic to the correct queue id and socket. - Note that if you are using the XDP_SHARED_UMEM option, it is - possible to switch traffic between any socket bound to the same - umem. - Q: My packets are sometimes corrupted. What is wrong? A: Care has to be taken not to feed the same buffer in the UMEM into diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index 0f9eda3187f3..161b43f1ce66 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -582,7 +582,7 @@ depending on the hardware. In all cases, however, only routes that have the Devices generating the streams may allow enabling and disabling some of the routes or have a fixed routing configuration. If the routes can be disabled, not declaring the routes (or declaring them without -``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will +``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag unset. diff --git a/MAINTAINERS b/MAINTAINERS index ac9668c479ee..33fa456699f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1107,7 +1107,6 @@ L: linux-pm@vger.kernel.org S: Supported F: Documentation/admin-guide/pm/amd-pstate.rst F: drivers/cpufreq/amd-pstate* -F: include/linux/amd-pstate.h F: tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py AMD PTDMA DRIVER @@ -8910,6 +8909,16 @@ S: Maintained F: sound/soc/fsl/fsl* F: sound/soc/fsl/imx* +FREESCALE SOC LPC32XX SOUND DRIVERS +M: J.M.B. Downing <jonathan.downing@nautel.com> +M: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com> +R: Vladimir Zapolskiy <vz@mleia.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml +F: sound/soc/fsl/lpc3xxx-* + FREESCALE SOC SOUND QMC DRIVER M: Herve Codina <herve.codina@bootlin.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -15238,7 +15247,6 @@ F: drivers/staging/most/ F: include/linux/most.h MOTORCOMM PHY DRIVER -M: Peter Geis <pgwipeout@gmail.com> M: Frank <Frank.Sae@motor-comm.com> L: netdev@vger.kernel.org S: Maintained @@ -16197,7 +16205,7 @@ NXP SGTL5000 DRIVER M: Fabio Estevam <festevam@gmail.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained -F: Documentation/devicetree/bindings/sound/sgtl5000.yaml +F: Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml F: sound/soc/codecs/sgtl5000* NXP SJA1105 ETHERNET SWITCH DRIVER @@ -22177,13 +22185,13 @@ M: Baojun Xu <baojun.xu@ti.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/sound/tas2552.txt -F: Documentation/devicetree/bindings/sound/tas2562.yaml -F: Documentation/devicetree/bindings/sound/tas2770.yaml -F: Documentation/devicetree/bindings/sound/tas27xx.yaml +F: Documentation/devicetree/bindings/sound/ti,tas2562.yaml +F: Documentation/devicetree/bindings/sound/ti,tas2770.yaml +F: Documentation/devicetree/bindings/sound/ti,tas27xx.yaml F: Documentation/devicetree/bindings/sound/ti,pcm1681.yaml F: Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml -F: Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +F: Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml F: Documentation/devicetree/bindings/sound/tlv320aic31xx.txt F: Documentation/devicetree/bindings/sound/tpa6130a2.txt F: include/sound/tas2*.h @@ -22680,7 +22688,7 @@ L: tomoyo-users-en@lists.osdn.me (subscribers-only, for users in English) L: tomoyo-dev@lists.osdn.me (subscribers-only, for developers in Japanese) L: tomoyo-users@lists.osdn.me (subscribers-only, for users in Japanese) S: Maintained -W: https://tomoyo.osdn.jp/ +W: https://tomoyo.sourceforge.net/ F: security/tomoyo/ TOPSTAR LAPTOP EXTRAS DRIVER @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 10 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Baby Opossum Posse # *DOCUMENTATION* diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index cf2480dce285..ac5ae621b2af 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -311,7 +311,6 @@ CONFIG_SND_IMX_SOC=y CONFIG_SND_SOC_EUKREA_TLV320=y CONFIG_SND_SOC_IMX_ES8328=y CONFIG_SND_SOC_IMX_SGTL5000=y -CONFIG_SND_SOC_IMX_SPDIF=y CONFIG_SND_SOC_FSL_ASOC_CARD=y CONFIG_SND_SOC_AC97_CODEC=y CONFIG_SND_SOC_CS42XX8_I2C=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 57a9abe78ee4..a6c9688fee0e 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -940,7 +940,6 @@ CONFIG_SND_SOC_FSL_MICFIL=m CONFIG_SND_SOC_FSL_EASRC=m CONFIG_SND_IMX_SOC=m CONFIG_SND_SOC_IMX_SGTL5000=m -CONFIG_SND_SOC_IMX_SPDIF=m CONFIG_SND_SOC_FSL_ASOC_CARD=m CONFIG_SND_SOC_IMX_AUDMIX=m CONFIG_SND_SOC_MT8183=m diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index e4546b29dd0c..fd87c4b8f984 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -146,7 +146,7 @@ /* Coprocessor traps */ .macro __init_el2_cptr __check_hvhe .LnVHE_\@, x1 - mov x0, #(CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN) + mov x0, #CPACR_ELx_FPEN msr cpacr_el1, x0 b .Lskip_set_cptr_\@ .LnVHE_\@: @@ -277,7 +277,7 @@ // (h)VHE case mrs x0, cpacr_el1 // Disable SVE traps - orr x0, x0, #(CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN) + orr x0, x0, #CPACR_ELx_ZEN msr cpacr_el1, x0 b .Lskip_set_cptr_\@ @@ -298,7 +298,7 @@ // (h)VHE case mrs x0, cpacr_el1 // Disable SME traps - orr x0, x0, #(CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN) + orr x0, x0, #CPACR_ELx_SMEN msr cpacr_el1, x0 b .Lskip_set_cptr_sme_\@ diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 4ff0ae3f6d66..41fd90895dfc 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -153,8 +153,9 @@ extern void __memset_io(volatile void __iomem *, int, size_t); * emit the large TLP from the CPU. */ -static inline void __const_memcpy_toio_aligned32(volatile u32 __iomem *to, - const u32 *from, size_t count) +static __always_inline void +__const_memcpy_toio_aligned32(volatile u32 __iomem *to, const u32 *from, + size_t count) { switch (count) { case 8: @@ -196,24 +197,22 @@ static inline void __const_memcpy_toio_aligned32(volatile u32 __iomem *to, void __iowrite32_copy_full(void __iomem *to, const void *from, size_t count); -static inline void __const_iowrite32_copy(void __iomem *to, const void *from, - size_t count) +static __always_inline void +__iowrite32_copy(void __iomem *to, const void *from, size_t count) { - if (count == 8 || count == 4 || count == 2 || count == 1) { + if (__builtin_constant_p(count) && + (count == 8 || count == 4 || count == 2 || count == 1)) { __const_memcpy_toio_aligned32(to, from, count); dgh(); } else { __iowrite32_copy_full(to, from, count); } } +#define __iowrite32_copy __iowrite32_copy -#define __iowrite32_copy(to, from, count) \ - (__builtin_constant_p(count) ? \ - __const_iowrite32_copy(to, from, count) : \ - __iowrite32_copy_full(to, from, count)) - -static inline void __const_memcpy_toio_aligned64(volatile u64 __iomem *to, - const u64 *from, size_t count) +static __always_inline void +__const_memcpy_toio_aligned64(volatile u64 __iomem *to, const u64 *from, + size_t count) { switch (count) { case 8: @@ -255,21 +254,18 @@ static inline void __const_memcpy_toio_aligned64(volatile u64 __iomem *to, void __iowrite64_copy_full(void __iomem *to, const void *from, size_t count); -static inline void __const_iowrite64_copy(void __iomem *to, const void *from, - size_t count) +static __always_inline void +__iowrite64_copy(void __iomem *to, const void *from, size_t count) { - if (count == 8 || count == 4 || count == 2 || count == 1) { + if (__builtin_constant_p(count) && + (count == 8 || count == 4 || count == 2 || count == 1)) { __const_memcpy_toio_aligned64(to, from, count); dgh(); } else { __iowrite64_copy_full(to, from, count); } } - -#define __iowrite64_copy(to, from, count) \ - (__builtin_constant_p(count) ? \ - __const_iowrite64_copy(to, from, count) : \ - __iowrite64_copy_full(to, from, count)) +#define __iowrite64_copy __iowrite64_copy /* * I/O memory mapping functions. diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index e01bb5ca13b7..b2adc2c6c82a 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -305,6 +305,12 @@ GENMASK(19, 14) | \ BIT(11)) +#define CPTR_VHE_EL2_RES0 (GENMASK(63, 32) | \ + GENMASK(27, 26) | \ + GENMASK(23, 22) | \ + GENMASK(19, 18) | \ + GENMASK(15, 0)) + /* Hyp Debug Configuration Register bits */ #define MDCR_EL2_E2TB_MASK (UL(0x3)) #define MDCR_EL2_E2TB_SHIFT (UL(24)) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 501e3e019c93..21650e7924d4 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -557,6 +557,68 @@ static __always_inline void kvm_incr_pc(struct kvm_vcpu *vcpu) vcpu_set_flag((v), e); \ } while (0) +#define __build_check_all_or_none(r, bits) \ + BUILD_BUG_ON(((r) & (bits)) && ((r) & (bits)) != (bits)) + +#define __cpacr_to_cptr_clr(clr, set) \ + ({ \ + u64 cptr = 0; \ + \ + if ((set) & CPACR_ELx_FPEN) \ + cptr |= CPTR_EL2_TFP; \ + if ((set) & CPACR_ELx_ZEN) \ + cptr |= CPTR_EL2_TZ; \ + if ((set) & CPACR_ELx_SMEN) \ + cptr |= CPTR_EL2_TSM; \ + if ((clr) & CPACR_ELx_TTA) \ + cptr |= CPTR_EL2_TTA; \ + if ((clr) & CPTR_EL2_TAM) \ + cptr |= CPTR_EL2_TAM; \ + if ((clr) & CPTR_EL2_TCPAC) \ + cptr |= CPTR_EL2_TCPAC; \ + \ + cptr; \ + }) + +#define __cpacr_to_cptr_set(clr, set) \ + ({ \ + u64 cptr = 0; \ + \ + if ((clr) & CPACR_ELx_FPEN) \ + cptr |= CPTR_EL2_TFP; \ + if ((clr) & CPACR_ELx_ZEN) \ + cptr |= CPTR_EL2_TZ; \ + if ((clr) & CPACR_ELx_SMEN) \ + cptr |= CPTR_EL2_TSM; \ + if ((set) & CPACR_ELx_TTA) \ + cptr |= CPTR_EL2_TTA; \ + if ((set) & CPTR_EL2_TAM) \ + cptr |= CPTR_EL2_TAM; \ + if ((set) & CPTR_EL2_TCPAC) \ + cptr |= CPTR_EL2_TCPAC; \ + \ + cptr; \ + }) + +#define cpacr_clear_set(clr, set) \ + do { \ + BUILD_BUG_ON((set) & CPTR_VHE_EL2_RES0); \ + BUILD_BUG_ON((clr) & CPACR_ELx_E0POE); \ + __build_check_all_or_none((clr), CPACR_ELx_FPEN); \ + __build_check_all_or_none((set), CPACR_ELx_FPEN); \ + __build_check_all_or_none((clr), CPACR_ELx_ZEN); \ + __build_check_all_or_none((set), CPACR_ELx_ZEN); \ + __build_check_all_or_none((clr), CPACR_ELx_SMEN); \ + __build_check_all_or_none((set), CPACR_ELx_SMEN); \ + \ + if (has_vhe() || has_hvhe()) \ + sysreg_clear_set(cpacr_el1, clr, set); \ + else \ + sysreg_clear_set(cptr_el2, \ + __cpacr_to_cptr_clr(clr, set), \ + __cpacr_to_cptr_set(clr, set));\ + } while (0) + static __always_inline void kvm_write_cptr_el2(u64 val) { if (has_vhe() || has_hvhe()) @@ -570,17 +632,16 @@ static __always_inline u64 kvm_get_reset_cptr_el2(struct kvm_vcpu *vcpu) u64 val; if (has_vhe()) { - val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN | - CPACR_EL1_ZEN_EL1EN); + val = (CPACR_ELx_FPEN | CPACR_EL1_ZEN_EL1EN); if (cpus_have_final_cap(ARM64_SME)) val |= CPACR_EL1_SMEN_EL1EN; } else if (has_hvhe()) { - val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN); + val = CPACR_ELx_FPEN; if (!vcpu_has_sve(vcpu) || !guest_owns_fp_regs()) - val |= CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN; + val |= CPACR_ELx_ZEN; if (cpus_have_final_cap(ARM64_SME)) - val |= CPACR_EL1_SMEN_EL1EN | CPACR_EL1_SMEN_EL0EN; + val |= CPACR_ELx_SMEN; } else { val = CPTR_NVHE_EL2_RES1; diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 8170c04fde91..36b8e97bf49e 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -76,6 +76,7 @@ static inline enum kvm_mode kvm_get_mode(void) { return KVM_MODE_NONE; }; DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use); extern unsigned int __ro_after_init kvm_sve_max_vl; +extern unsigned int __ro_after_init kvm_host_sve_max_vl; int __init kvm_arm_init_sve(void); u32 __attribute_const__ kvm_target_cpu(void); @@ -521,6 +522,20 @@ struct kvm_cpu_context { u64 *vncr_array; }; +struct cpu_sve_state { + __u64 zcr_el1; + + /* + * Ordering is important since __sve_save_state/__sve_restore_state + * relies on it. + */ + __u32 fpsr; + __u32 fpcr; + + /* Must be SVE_VQ_BYTES (128 bit) aligned. */ + __u8 sve_regs[]; +}; + /* * This structure is instantiated on a per-CPU basis, and contains * data that is: @@ -534,7 +549,15 @@ struct kvm_cpu_context { */ struct kvm_host_data { struct kvm_cpu_context host_ctxt; - struct user_fpsimd_state *fpsimd_state; /* hyp VA */ + + /* + * All pointers in this union are hyp VA. + * sve_state is only used in pKVM and if system_supports_sve(). + */ + union { + struct user_fpsimd_state *fpsimd_state; + struct cpu_sve_state *sve_state; + }; /* Ownership of the FP regs */ enum { diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 3e80464f8953..b05bceca3385 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -111,7 +111,8 @@ void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu); void __fpsimd_save_state(struct user_fpsimd_state *fp_regs); void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs); -void __sve_restore_state(void *sve_pffr, u32 *fpsr); +void __sve_save_state(void *sve_pffr, u32 *fpsr, int save_ffr); +void __sve_restore_state(void *sve_pffr, u32 *fpsr, int restore_ffr); u64 __guest_enter(struct kvm_vcpu *vcpu); @@ -142,5 +143,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val); extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); +extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index ad9cfb5c1ff4..cd56acd9a842 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -128,4 +128,13 @@ static inline unsigned long hyp_ffa_proxy_pages(void) return (2 * KVM_FFA_MBOX_NR_PAGES) + DIV_ROUND_UP(desc_max, PAGE_SIZE); } +static inline size_t pkvm_host_sve_state_size(void) +{ + if (!system_supports_sve()) + return 0; + + return size_add(sizeof(struct cpu_sve_state), + SVE_SIG_REGS_SIZE(sve_vq_from_vl(kvm_host_sve_max_vl))); +} + #endif /* __ARM64_KVM_PKVM_H__ */ diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index dd6ce86d4332..b776e7424fe9 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -462,6 +462,9 @@ static int run_all_insn_set_hw_mode(unsigned int cpu) for (int i = 0; i < ARRAY_SIZE(insn_emulations); i++) { struct insn_emulation *insn = insn_emulations[i]; bool enable = READ_ONCE(insn->current_mode) == INSN_HW; + if (insn->status == INSN_UNAVAILABLE) + continue; + if (insn->set_hw_mode && insn->set_hw_mode(enable)) { pr_warn("CPU[%u] cannot support the emulation of %s", cpu, insn->name); diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 9996a989b52e..59716789fe0f 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1931,6 +1931,11 @@ static unsigned long nvhe_percpu_order(void) return size ? get_order(size) : 0; } +static size_t pkvm_host_sve_state_order(void) +{ + return get_order(pkvm_host_sve_state_size()); +} + /* A lookup table holding the hypervisor VA for each vector slot */ static void *hyp_spectre_vector_selector[BP_HARDEN_EL2_SLOTS]; @@ -2310,12 +2315,20 @@ static void __init teardown_subsystems(void) static void __init teardown_hyp_mode(void) { + bool free_sve = system_supports_sve() && is_protected_kvm_enabled(); int cpu; free_hyp_pgds(); for_each_possible_cpu(cpu) { free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); free_pages(kvm_nvhe_sym(kvm_arm_hyp_percpu_base)[cpu], nvhe_percpu_order()); + + if (free_sve) { + struct cpu_sve_state *sve_state; + + sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; + free_pages((unsigned long) sve_state, pkvm_host_sve_state_order()); + } } } @@ -2398,6 +2411,58 @@ static int __init kvm_hyp_init_protection(u32 hyp_va_bits) return 0; } +static int init_pkvm_host_sve_state(void) +{ + int cpu; + + if (!system_supports_sve()) + return 0; + + /* Allocate pages for host sve state in protected mode. */ + for_each_possible_cpu(cpu) { + struct page *page = alloc_pages(GFP_KERNEL, pkvm_host_sve_state_order()); + + if (!page) + return -ENOMEM; + + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = page_address(page); + } + + /* + * Don't map the pages in hyp since these are only used in protected + * mode, which will (re)create its own mapping when initialized. + */ + + return 0; +} + +/* + * Finalizes the initialization of hyp mode, once everything else is initialized + * and the initialziation process cannot fail. + */ +static void finalize_init_hyp_mode(void) +{ + int cpu; + + if (system_supports_sve() && is_protected_kvm_enabled()) { + for_each_possible_cpu(cpu) { + struct cpu_sve_state *sve_state; + + sve_state = per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state; + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->sve_state = + kern_hyp_va(sve_state); + } + } else { + for_each_possible_cpu(cpu) { + struct user_fpsimd_state *fpsimd_state; + + fpsimd_state = &per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->host_ctxt.fp_regs; + per_cpu_ptr_nvhe_sym(kvm_host_data, cpu)->fpsimd_state = + kern_hyp_va(fpsimd_state); + } + } +} + static void pkvm_hyp_init_ptrauth(void) { struct kvm_cpu_context *hyp_ctxt; @@ -2566,6 +2631,10 @@ static int __init init_hyp_mode(void) goto out_err; } + err = init_pkvm_host_sve_state(); + if (err) + goto out_err; + err = kvm_hyp_init_protection(hyp_va_bits); if (err) { kvm_err("Failed to init hyp memory protection\n"); @@ -2730,6 +2799,13 @@ static __init int kvm_arm_init(void) if (err) goto out_subs; + /* + * This should be called after initialization is done and failure isn't + * possible anymore. + */ + if (!in_hyp_mode) + finalize_init_hyp_mode(); + kvm_arm_initialised = true; return 0; diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 72d733c74a38..54090967a335 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2181,16 +2181,23 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) if (forward_traps(vcpu, HCR_NV)) return; + spsr = vcpu_read_sys_reg(vcpu, SPSR_EL2); + spsr = kvm_check_illegal_exception_return(vcpu, spsr); + /* Check for an ERETAx */ esr = kvm_vcpu_get_esr(vcpu); if (esr_iss_is_eretax(esr) && !kvm_auth_eretax(vcpu, &elr)) { /* - * Oh no, ERETAx failed to authenticate. If we have - * FPACCOMBINE, deliver an exception right away. If we - * don't, then let the mangled ELR value trickle down the + * Oh no, ERETAx failed to authenticate. + * + * If we have FPACCOMBINE and we don't have a pending + * Illegal Execution State exception (which has priority + * over FPAC), deliver an exception right away. + * + * Otherwise, let the mangled ELR value trickle down the * ERET handling, and the guest will have a little surprise. */ - if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE)) { + if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE) && !(spsr & PSR_IL_BIT)) { esr &= ESR_ELx_ERET_ISS_ERETA; esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_FPAC); kvm_inject_nested_sync(vcpu, esr); @@ -2201,17 +2208,11 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu) preempt_disable(); kvm_arch_vcpu_put(vcpu); - spsr = __vcpu_sys_reg(vcpu, SPSR_EL2); - spsr = kvm_check_illegal_exception_return(vcpu, spsr); if (!esr_iss_is_eretax(esr)) elr = __vcpu_sys_reg(vcpu, ELR_EL2); trace_kvm_nested_eret(vcpu, elr, spsr); - /* - * Note that the current exception level is always the virtual EL2, - * since we set HCR_EL2.NV bit only when entering the virtual EL2. - */ *vcpu_pc(vcpu) = elr; *vcpu_cpsr(vcpu) = spsr; diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index 1807d3a79a8a..521b32868d0d 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -90,6 +90,13 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) fpsimd_save_and_flush_cpu_state(); } } + + /* + * If normal guests gain SME support, maintain this behavior for pKVM + * guests, which don't support SME. + */ + WARN_ON(is_protected_kvm_enabled() && system_supports_sme() && + read_sysreg_s(SYS_SVCR)); } /* @@ -161,9 +168,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) if (has_vhe() && system_supports_sme()) { /* Also restore EL0 state seen on entry */ if (vcpu_get_flag(vcpu, HOST_SME_ENABLED)) - sysreg_clear_set(CPACR_EL1, 0, - CPACR_EL1_SMEN_EL0EN | - CPACR_EL1_SMEN_EL1EN); + sysreg_clear_set(CPACR_EL1, 0, CPACR_ELx_SMEN); else sysreg_clear_set(CPACR_EL1, CPACR_EL1_SMEN_EL0EN, diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index e2f762d959bb..11098eb7eb44 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -251,6 +251,7 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) case PSR_AA32_MODE_SVC: case PSR_AA32_MODE_ABT: case PSR_AA32_MODE_UND: + case PSR_AA32_MODE_SYS: if (!vcpu_el1_is_32bit(vcpu)) return -EINVAL; break; @@ -276,7 +277,7 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if (*vcpu_cpsr(vcpu) & PSR_MODE32_BIT) { int i, nr_reg; - switch (*vcpu_cpsr(vcpu)) { + switch (*vcpu_cpsr(vcpu) & PSR_AA32_MODE_MASK) { /* * Either we are dealing with user mode, and only the * first 15 registers (+ PC) must be narrowed to 32bit. diff --git a/arch/arm64/kvm/hyp/aarch32.c b/arch/arm64/kvm/hyp/aarch32.c index 8d9670e6615d..449fa58cf3b6 100644 --- a/arch/arm64/kvm/hyp/aarch32.c +++ b/arch/arm64/kvm/hyp/aarch32.c @@ -50,9 +50,23 @@ bool kvm_condition_valid32(const struct kvm_vcpu *vcpu) u32 cpsr_cond; int cond; - /* Top two bits non-zero? Unconditional. */ - if (kvm_vcpu_get_esr(vcpu) >> 30) + /* + * These are the exception classes that could fire with a + * conditional instruction. + */ + switch (kvm_vcpu_trap_get_class(vcpu)) { + case ESR_ELx_EC_CP15_32: + case ESR_ELx_EC_CP15_64: + case ESR_ELx_EC_CP14_MR: + case ESR_ELx_EC_CP14_LS: + case ESR_ELx_EC_FP_ASIMD: + case ESR_ELx_EC_CP10_ID: + case ESR_ELx_EC_CP14_64: + case ESR_ELx_EC_SVC32: + break; + default: return true; + } /* Is condition field valid? */ cond = kvm_vcpu_get_condition(vcpu); diff --git a/arch/arm64/kvm/hyp/fpsimd.S b/arch/arm64/kvm/hyp/fpsimd.S index 61e6f3ba7b7d..e950875e31ce 100644 --- a/arch/arm64/kvm/hyp/fpsimd.S +++ b/arch/arm64/kvm/hyp/fpsimd.S @@ -25,3 +25,9 @@ SYM_FUNC_START(__sve_restore_state) sve_load 0, x1, x2, 3 ret SYM_FUNC_END(__sve_restore_state) + +SYM_FUNC_START(__sve_save_state) + mov x2, #1 + sve_save 0, x1, x2, 3 + ret +SYM_FUNC_END(__sve_save_state) diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index a92566f36022..0c4de44534b7 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -316,10 +316,24 @@ static inline void __hyp_sve_restore_guest(struct kvm_vcpu *vcpu) { sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); __sve_restore_state(vcpu_sve_pffr(vcpu), - &vcpu->arch.ctxt.fp_regs.fpsr); + &vcpu->arch.ctxt.fp_regs.fpsr, + true); write_sysreg_el1(__vcpu_sys_reg(vcpu, ZCR_EL1), SYS_ZCR); } +static inline void __hyp_sve_save_host(void) +{ + struct cpu_sve_state *sve_state = *host_data_ptr(sve_state); + + sve_state->zcr_el1 = read_sysreg_el1(SYS_ZCR); + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); + __sve_save_state(sve_state->sve_regs + sve_ffr_offset(kvm_host_sve_max_vl), + &sve_state->fpsr, + true); +} + +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu); + /* * We trap the first access to the FP/SIMD to save the host context and * restore the guest context lazily. @@ -330,7 +344,6 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) { bool sve_guest; u8 esr_ec; - u64 reg; if (!system_supports_fpsimd()) return false; @@ -353,24 +366,15 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) /* Valid trap. Switch the context: */ /* First disable enough traps to allow us to update the registers */ - if (has_vhe() || has_hvhe()) { - reg = CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN; - if (sve_guest) - reg |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; - - sysreg_clear_set(cpacr_el1, 0, reg); - } else { - reg = CPTR_EL2_TFP; - if (sve_guest) - reg |= CPTR_EL2_TZ; - - sysreg_clear_set(cptr_el2, reg, 0); - } + if (sve_guest || (is_protected_kvm_enabled() && system_supports_sve())) + cpacr_clear_set(0, CPACR_ELx_FPEN | CPACR_ELx_ZEN); + else + cpacr_clear_set(0, CPACR_ELx_FPEN); isb(); /* Write out the host state if it's in the registers */ if (host_owns_fp_regs()) - __fpsimd_save_state(*host_data_ptr(fpsimd_state)); + kvm_hyp_save_fpsimd_host(vcpu); /* Restore the guest state */ if (sve_guest) diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 22f374e9f532..24a9a8330d19 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -59,7 +59,6 @@ static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu) } void pkvm_hyp_vm_table_init(void *tbl); -void pkvm_host_fpsimd_state_init(void); int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva, unsigned long pgd_hva); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index d5c48dc98f67..f43d845f3c4e 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -23,20 +23,80 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); +static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) +{ + __vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_el1(SYS_ZCR); + /* + * On saving/restoring guest sve state, always use the maximum VL for + * the guest. The layout of the data when saving the sve state depends + * on the VL, so use a consistent (i.e., the maximum) guest VL. + */ + sve_cond_update_zcr_vq(vcpu_sve_max_vq(vcpu) - 1, SYS_ZCR_EL2); + __sve_save_state(vcpu_sve_pffr(vcpu), &vcpu->arch.ctxt.fp_regs.fpsr, true); + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); +} + +static void __hyp_sve_restore_host(void) +{ + struct cpu_sve_state *sve_state = *host_data_ptr(sve_state); + + /* + * On saving/restoring host sve state, always use the maximum VL for + * the host. The layout of the data when saving the sve state depends + * on the VL, so use a consistent (i.e., the maximum) host VL. + * + * Setting ZCR_EL2 to ZCR_ELx_LEN_MASK sets the effective length + * supported by the system (or limited at EL3). + */ + write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); + __sve_restore_state(sve_state->sve_regs + sve_ffr_offset(kvm_host_sve_max_vl), + &sve_state->fpsr, + true); + write_sysreg_el1(sve_state->zcr_el1, SYS_ZCR); +} + +static void fpsimd_sve_flush(void) +{ + *host_data_ptr(fp_owner) = FP_STATE_HOST_OWNED; +} + +static void fpsimd_sve_sync(struct kvm_vcpu *vcpu) +{ + if (!guest_owns_fp_regs()) + return; + + cpacr_clear_set(0, CPACR_ELx_FPEN | CPACR_ELx_ZEN); + isb(); + + if (vcpu_has_sve(vcpu)) + __hyp_sve_save_guest(vcpu); + else + __fpsimd_save_state(&vcpu->arch.ctxt.fp_regs); + + if (system_supports_sve()) + __hyp_sve_restore_host(); + else + __fpsimd_restore_state(*host_data_ptr(fpsimd_state)); + + *host_data_ptr(fp_owner) = FP_STATE_HOST_OWNED; +} + static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) { struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu; + fpsimd_sve_flush(); + hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state); - hyp_vcpu->vcpu.arch.sve_max_vl = host_vcpu->arch.sve_max_vl; + /* Limit guest vector length to the maximum supported by the host. */ + hyp_vcpu->vcpu.arch.sve_max_vl = min(host_vcpu->arch.sve_max_vl, kvm_host_sve_max_vl); hyp_vcpu->vcpu.arch.hw_mmu = host_vcpu->arch.hw_mmu; hyp_vcpu->vcpu.arch.hcr_el2 = host_vcpu->arch.hcr_el2; hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; - hyp_vcpu->vcpu.arch.cptr_el2 = host_vcpu->arch.cptr_el2; hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags; @@ -54,10 +114,11 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu) struct vgic_v3_cpu_if *host_cpu_if = &host_vcpu->arch.vgic_cpu.vgic_v3; unsigned int i; + fpsimd_sve_sync(&hyp_vcpu->vcpu); + host_vcpu->arch.ctxt = hyp_vcpu->vcpu.arch.ctxt; host_vcpu->arch.hcr_el2 = hyp_vcpu->vcpu.arch.hcr_el2; - host_vcpu->arch.cptr_el2 = hyp_vcpu->vcpu.arch.cptr_el2; host_vcpu->arch.fault = hyp_vcpu->vcpu.arch.fault; @@ -79,6 +140,17 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt) struct pkvm_hyp_vcpu *hyp_vcpu; struct kvm *host_kvm; + /* + * KVM (and pKVM) doesn't support SME guests for now, and + * ensures that SME features aren't enabled in pstate when + * loading a vcpu. Therefore, if SME features enabled the host + * is misbehaving. + */ + if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR))) { + ret = -EINVAL; + goto out; + } + host_kvm = kern_hyp_va(host_vcpu->kvm); hyp_vcpu = pkvm_load_hyp_vcpu(host_kvm->arch.pkvm.handle, host_vcpu->vcpu_idx); @@ -405,11 +477,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) handle_host_smc(host_ctxt); break; case ESR_ELx_EC_SVE: - if (has_hvhe()) - sysreg_clear_set(cpacr_el1, 0, (CPACR_EL1_ZEN_EL1EN | - CPACR_EL1_ZEN_EL0EN)); - else - sysreg_clear_set(cptr_el2, CPTR_EL2_TZ, 0); + cpacr_clear_set(0, CPACR_ELx_ZEN); isb(); sve_cond_update_zcr_vq(ZCR_ELx_LEN_MASK, SYS_ZCR_EL2); break; diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 16aa4875ddb8..95cf18574251 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -18,6 +18,8 @@ unsigned long __icache_flags; /* Used by kvm_get_vttbr(). */ unsigned int kvm_arm_vmid_bits; +unsigned int kvm_host_sve_max_vl; + /* * Set trap register values based on features in ID_AA64PFR0. */ @@ -63,7 +65,7 @@ static void pvm_init_traps_aa64pfr0(struct kvm_vcpu *vcpu) /* Trap SVE */ if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE), feature_ids)) { if (has_hvhe()) - cptr_clear |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; + cptr_clear |= CPACR_ELx_ZEN; else cptr_set |= CPTR_EL2_TZ; } @@ -247,17 +249,6 @@ void pkvm_hyp_vm_table_init(void *tbl) vm_table = tbl; } -void pkvm_host_fpsimd_state_init(void) -{ - unsigned long i; - - for (i = 0; i < hyp_nr_cpus; i++) { - struct kvm_host_data *host_data = per_cpu_ptr(&kvm_host_data, i); - - host_data->fpsimd_state = &host_data->host_ctxt.fp_regs; - } -} - /* * Return the hyp vm structure corresponding to the handle. */ @@ -586,6 +577,8 @@ unlock: if (ret) unmap_donated_memory(hyp_vcpu, sizeof(*hyp_vcpu)); + hyp_vcpu->vcpu.arch.cptr_el2 = kvm_get_reset_cptr_el2(&hyp_vcpu->vcpu); + return ret; } diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 859f22f754d3..f4350ba07b0b 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -67,6 +67,28 @@ static int divide_memory_pool(void *virt, unsigned long size) return 0; } +static int pkvm_create_host_sve_mappings(void) +{ + void *start, *end; + int ret, i; + + if (!system_supports_sve()) + return 0; + + for (i = 0; i < hyp_nr_cpus; i++) { + struct kvm_host_data *host_data = per_cpu_ptr(&kvm_host_data, i); + struct cpu_sve_state *sve_state = host_data->sve_state; + + start = kern_hyp_va(sve_state); + end = start + PAGE_ALIGN(pkvm_host_sve_state_size()); + ret = pkvm_create_mappings(start, end, PAGE_HYP); + if (ret) + return ret; + } + + return 0; +} + static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, unsigned long *per_cpu_base, u32 hyp_va_bits) @@ -125,6 +147,8 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, return ret; } + pkvm_create_host_sve_mappings(); + /* * Map the host sections RO in the hypervisor, but transfer the * ownership from the host to the hypervisor itself to make sure they @@ -300,7 +324,6 @@ void __noreturn __pkvm_init_finalise(void) goto out; pkvm_hyp_vm_table_init(vm_table_base); - pkvm_host_fpsimd_state_init(); out: /* * We tail-called to here from handle___pkvm_init() and will not return, diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 6758cd905570..6af179c6356d 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -48,15 +48,14 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val |= has_hvhe() ? CPACR_EL1_TTA : CPTR_EL2_TTA; if (cpus_have_final_cap(ARM64_SME)) { if (has_hvhe()) - val &= ~(CPACR_EL1_SMEN_EL1EN | CPACR_EL1_SMEN_EL0EN); + val &= ~CPACR_ELx_SMEN; else val |= CPTR_EL2_TSM; } if (!guest_owns_fp_regs()) { if (has_hvhe()) - val &= ~(CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN | - CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN); + val &= ~(CPACR_ELx_FPEN | CPACR_ELx_ZEN); else val |= CPTR_EL2_TFP | CPTR_EL2_TZ; @@ -182,6 +181,25 @@ static bool kvm_handle_pvm_sys64(struct kvm_vcpu *vcpu, u64 *exit_code) kvm_handle_pvm_sysreg(vcpu, exit_code)); } +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) +{ + /* + * Non-protected kvm relies on the host restoring its sve state. + * Protected kvm restores the host's sve state as not to reveal that + * fpsimd was used by a guest nor leak upper sve bits. + */ + if (unlikely(is_protected_kvm_enabled() && system_supports_sve())) { + __hyp_sve_save_host(); + + /* Re-enable SVE traps if not supported for the guest vcpu. */ + if (!vcpu_has_sve(vcpu)) + cpacr_clear_set(CPACR_ELx_ZEN, 0); + + } else { + __fpsimd_save_state(*host_data_ptr(fpsimd_state)); + } +} + static const exit_handler_fn hyp_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = NULL, [ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32, diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index d7af5f46f22a..8fbb6a2e0559 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -93,8 +93,7 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val = read_sysreg(cpacr_el1); val |= CPACR_ELx_TTA; - val &= ~(CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN | - CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN); + val &= ~(CPACR_ELx_ZEN | CPACR_ELx_SMEN); /* * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to @@ -109,9 +108,9 @@ static void __activate_traps(struct kvm_vcpu *vcpu) if (guest_owns_fp_regs()) { if (vcpu_has_sve(vcpu)) - val |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; + val |= CPACR_ELx_ZEN; } else { - val &= ~(CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN); + val &= ~CPACR_ELx_FPEN; __activate_traps_fpsimd32(vcpu); } @@ -262,6 +261,11 @@ static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code) return true; } +static void kvm_hyp_save_fpsimd_host(struct kvm_vcpu *vcpu) +{ + __fpsimd_save_state(*host_data_ptr(fpsimd_state)); +} + static const exit_handler_fn hyp_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = NULL, [ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32, diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 6813c7c7f00a..bae8536cbf00 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -58,8 +58,10 @@ static u64 limit_nv_id_reg(u32 id, u64 val) break; case SYS_ID_AA64PFR1_EL1: - /* Only support SSBS */ - val &= NV_FTR(PFR1, SSBS); + /* Only support BTI, SSBS, CSV2_frac */ + val &= (NV_FTR(PFR1, BT) | + NV_FTR(PFR1, SSBS) | + NV_FTR(PFR1, CSV2_frac)); break; case SYS_ID_AA64MMFR0_EL1: diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index 1b7b58cb121f..3fc8ca164dbe 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -32,6 +32,7 @@ /* Maximum phys_shift supported for any VM on this host */ static u32 __ro_after_init kvm_ipa_limit; +unsigned int __ro_after_init kvm_host_sve_max_vl; /* * ARMv8 Reset Values @@ -51,6 +52,8 @@ int __init kvm_arm_init_sve(void) { if (system_supports_sve()) { kvm_sve_max_vl = sve_max_virtualisable_vl(); + kvm_host_sve_max_vl = sve_max_vl(); + kvm_nvhe_sym(kvm_host_sve_max_vl) = kvm_host_sve_max_vl; /* * The get_sve_reg()/set_sve_reg() ioctl interface will need diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c index 9f9486de0004..a3edced29ac1 100644 --- a/arch/arm64/mm/contpte.c +++ b/arch/arm64/mm/contpte.c @@ -376,7 +376,7 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma, * clearing access/dirty for the whole block. */ unsigned long start = addr; - unsigned long end = start + nr; + unsigned long end = start + nr * PAGE_SIZE; if (pte_cont(__ptep_get(ptep + nr - 1))) end = ALIGN(end, CONT_PTE_SIZE); @@ -386,7 +386,7 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma, ptep = contpte_align_down(ptep); } - __clear_young_dirty_ptes(vma, start, ptep, end - start, flags); + __clear_young_dirty_ptes(vma, start, ptep, (end - start) / PAGE_SIZE, flags); } EXPORT_SYMBOL_GPL(contpte_clear_young_dirty_ptes); diff --git a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts index 8aefb0c12672..a34734a6c3ce 100644 --- a/arch/loongarch/boot/dts/loongson-2k0500-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k0500-ref.dts @@ -44,14 +44,14 @@ &gmac0 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; bus_id = <0x0>; }; &gmac1 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; bus_id = <0x1>; }; diff --git a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts index 8463fe035386..23cf26cc3e5f 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k1000-ref.dts @@ -43,7 +43,7 @@ &gmac0 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy0>; mdio { compatible = "snps,dwmac-mdio"; @@ -58,7 +58,7 @@ &gmac1 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy1>; mdio { compatible = "snps,dwmac-mdio"; diff --git a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts index 74b99bd234cc..ea9e6985d0e9 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000-ref.dts +++ b/arch/loongarch/boot/dts/loongson-2k2000-ref.dts @@ -92,7 +92,7 @@ &gmac2 { status = "okay"; - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; phy-handle = <&phy2>; mdio { compatible = "snps,dwmac-mdio"; diff --git a/arch/loongarch/include/asm/numa.h b/arch/loongarch/include/asm/numa.h index 27f319b49862..b5f9de9f102e 100644 --- a/arch/loongarch/include/asm/numa.h +++ b/arch/loongarch/include/asm/numa.h @@ -56,6 +56,7 @@ extern int early_cpu_to_node(int cpu); static inline void early_numa_add_cpu(int cpuid, s16 node) { } static inline void numa_add_cpu(unsigned int cpu) { } static inline void numa_remove_cpu(unsigned int cpu) { } +static inline void set_cpuid_to_node(int cpuid, s16 node) { } static inline int early_cpu_to_node(int cpu) { diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 45b507a7b06f..d9eafd3ee3d1 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -42,7 +42,7 @@ .macro JUMP_VIRT_ADDR temp1 temp2 li.d \temp1, CACHE_BASE pcaddi \temp2, 0 - or \temp1, \temp1, \temp2 + bstrins.d \temp1, \temp2, (DMW_PABITS - 1), 0 jirl zero, \temp1, 0xc .endm diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index c4f7de2e2805..4677ea8fa8e9 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -22,7 +22,7 @@ _head: .word MZ_MAGIC /* "MZ", MS-DOS header */ .org 0x8 - .dword kernel_entry /* Kernel entry point */ + .dword _kernel_entry /* Kernel entry point (physical address) */ .dword _kernel_asize /* Kernel image effective size */ .quad PHYS_LINK_KADDR /* Kernel image load offset from start of RAM */ .org 0x38 /* 0x20 ~ 0x37 reserved */ diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 60e0fe97f61a..3d048f1be143 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -282,7 +282,7 @@ static void __init fdt_setup(void) return; /* Prefer to use built-in dtb, checking its legality first. */ - if (!fdt_check_header(__dtb_start)) + if (IS_ENABLED(CONFIG_BUILTIN_DTB) && !fdt_check_header(__dtb_start)) fdt_pointer = __dtb_start; else fdt_pointer = efi_fdt_pointer(); /* Fallback to firmware dtb */ @@ -351,10 +351,8 @@ void __init platform_init(void) arch_reserve_vmcore(); arch_reserve_crashkernel(); -#ifdef CONFIG_ACPI_TABLE_UPGRADE - acpi_table_upgrade(); -#endif #ifdef CONFIG_ACPI + acpi_table_upgrade(); acpi_gbl_use_default_register_widths = false; acpi_boot_table_init(); #endif diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 0dfe2388ef41..1436d2465939 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -273,7 +273,6 @@ static void __init fdt_smp_setup(void) if (cpuid == loongson_sysconf.boot_cpu_id) { cpu = 0; - numa_add_cpu(cpu); } else { cpu = cpumask_next_zero(-1, cpu_present_mask); } @@ -283,6 +282,9 @@ static void __init fdt_smp_setup(void) set_cpu_present(cpu, true); __cpu_number_map[cpuid] = cpu; __cpu_logical_map[cpu] = cpuid; + + early_numa_add_cpu(cpu, 0); + set_cpuid_to_node(cpuid, 0); } loongson_sysconf.nr_cpus = num_processors; @@ -468,6 +470,7 @@ void smp_prepare_boot_cpu(void) set_cpu_possible(0, true); set_cpu_online(0, true); set_my_cpu_offset(per_cpu_offset(0)); + numa_add_cpu(0); rr_node = first_node(node_online_map); for_each_possible_cpu(cpu) { diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index e8e97dbf9ca4..3c7595342730 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -6,6 +6,7 @@ #define PAGE_SIZE _PAGE_SIZE #define RO_EXCEPTION_TABLE_ALIGN 4 +#define PHYSADDR_MASK 0xffffffffffff /* 48-bit */ /* * Put .bss..swapper_pg_dir as the first thing in .bss. This will @@ -142,10 +143,11 @@ SECTIONS #ifdef CONFIG_EFI_STUB /* header symbols */ - _kernel_asize = _end - _text; - _kernel_fsize = _edata - _text; - _kernel_vsize = _end - __initdata_begin; - _kernel_rsize = _edata - __initdata_begin; + _kernel_entry = ABSOLUTE(kernel_entry & PHYSADDR_MASK); + _kernel_asize = ABSOLUTE(_end - _text); + _kernel_fsize = ABSOLUTE(_edata - _text); + _kernel_vsize = ABSOLUTE(_end - __initdata_begin); + _kernel_rsize = ABSOLUTE(_edata - __initdata_begin); #endif .gptab.sdata : { diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c index 0eb689351b7d..5cd407c6a8e4 100644 --- a/arch/riscv/kvm/aia_device.c +++ b/arch/riscv/kvm/aia_device.c @@ -237,10 +237,11 @@ static gpa_t aia_imsic_ppn(struct kvm_aia *aia, gpa_t addr) static u32 aia_imsic_hart_index(struct kvm_aia *aia, gpa_t addr) { - u32 hart, group = 0; + u32 hart = 0, group = 0; - hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & - GENMASK_ULL(aia->nr_hart_bits - 1, 0); + if (aia->nr_hart_bits) + hart = (addr >> (aia->nr_guest_bits + IMSIC_MMIO_PAGE_SHIFT)) & + GENMASK_ULL(aia->nr_hart_bits - 1, 0); if (aia->nr_group_bits) group = (addr >> aia->nr_group_shift) & GENMASK_ULL(aia->nr_group_bits - 1, 0); diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index c676275ea0a0..62874fbca29f 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -724,9 +724,9 @@ static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, switch (reg_subtype) { case KVM_REG_RISCV_ISA_SINGLE: return riscv_vcpu_set_isa_ext_single(vcpu, reg_num, reg_val); - case KVM_REG_RISCV_SBI_MULTI_EN: + case KVM_REG_RISCV_ISA_MULTI_EN: return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, true); - case KVM_REG_RISCV_SBI_MULTI_DIS: + case KVM_REG_RISCV_ISA_MULTI_DIS: return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, false); default: return -ENOENT; diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index b3fcf7d67efb..5224f3733802 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -293,8 +293,8 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(access_error(cause, vma))) { vma_end_read(vma); count_vm_vma_lock_event(VMA_LOCK_SUCCESS); - tsk->thread.bad_cause = SEGV_ACCERR; - bad_area_nosemaphore(regs, code, addr); + tsk->thread.bad_cause = cause; + bad_area_nosemaphore(regs, SEGV_ACCERR, addr); return; } diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index e3218d65f21d..e3405e4b99af 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -250,18 +250,19 @@ static void __init setup_bootmem(void) kernel_map.va_pa_offset = PAGE_OFFSET - phys_ram_base; /* - * memblock allocator is not aware of the fact that last 4K bytes of - * the addressable memory can not be mapped because of IS_ERR_VALUE - * macro. Make sure that last 4k bytes are not usable by memblock - * if end of dram is equal to maximum addressable memory. For 64-bit - * kernel, this problem can't happen here as the end of the virtual - * address space is occupied by the kernel mapping then this check must - * be done as soon as the kernel mapping base address is determined. + * Reserve physical address space that would be mapped to virtual + * addresses greater than (void *)(-PAGE_SIZE) because: + * - This memory would overlap with ERR_PTR + * - This memory belongs to high memory, which is not supported + * + * This is not applicable to 64-bit kernel, because virtual addresses + * after (void *)(-PAGE_SIZE) are not linearly mapped: they are + * occupied by kernel mapping. Also it is unrealistic for high memory + * to exist on 64-bit platforms. */ if (!IS_ENABLED(CONFIG_64BIT)) { - max_mapped_addr = __pa(~(ulong)0); - if (max_mapped_addr == (phys_ram_end - 1)) - memblock_set_current_limit(max_mapped_addr - 4096); + max_mapped_addr = __va_to_pa_nodebug(-PAGE_SIZE); + memblock_reserve(max_mapped_addr, (phys_addr_t)-max_mapped_addr); } min_low_pfn = PFN_UP(phys_ram_base); diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index 9863ebe75019..edae13416196 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -451,7 +451,7 @@ static void *nt_final(void *ptr) /* * Initialize ELF header (new kernel) */ -static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt) +static void *ehdr_init(Elf64_Ehdr *ehdr, int phdr_count) { memset(ehdr, 0, sizeof(*ehdr)); memcpy(ehdr->e_ident, ELFMAG, SELFMAG); @@ -465,11 +465,8 @@ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt) ehdr->e_phoff = sizeof(Elf64_Ehdr); ehdr->e_ehsize = sizeof(Elf64_Ehdr); ehdr->e_phentsize = sizeof(Elf64_Phdr); - /* - * Number of memory chunk PT_LOAD program headers plus one kernel - * image PT_LOAD program header plus one PT_NOTE program header. - */ - ehdr->e_phnum = mem_chunk_cnt + 1 + 1; + /* Number of PT_LOAD program headers plus PT_NOTE program header */ + ehdr->e_phnum = phdr_count + 1; return ehdr + 1; } @@ -503,12 +500,14 @@ static int get_mem_chunk_cnt(void) /* * Initialize ELF loads (new kernel) */ -static void loads_init(Elf64_Phdr *phdr) +static void loads_init(Elf64_Phdr *phdr, bool os_info_has_vm) { - unsigned long old_identity_base = os_info_old_value(OS_INFO_IDENTITY_BASE); + unsigned long old_identity_base = 0; phys_addr_t start, end; u64 idx; + if (os_info_has_vm) + old_identity_base = os_info_old_value(OS_INFO_IDENTITY_BASE); for_each_physmem_range(idx, &oldmem_type, &start, &end) { phdr->p_type = PT_LOAD; phdr->p_vaddr = old_identity_base + start; @@ -522,6 +521,11 @@ static void loads_init(Elf64_Phdr *phdr) } } +static bool os_info_has_vm(void) +{ + return os_info_old_value(OS_INFO_KASLR_OFFSET); +} + /* * Prepare PT_LOAD type program header for kernel image region */ @@ -566,7 +570,7 @@ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) return ptr; } -static size_t get_elfcorehdr_size(int mem_chunk_cnt) +static size_t get_elfcorehdr_size(int phdr_count) { size_t size; @@ -581,10 +585,8 @@ static size_t get_elfcorehdr_size(int mem_chunk_cnt) size += nt_vmcoreinfo_size(); /* nt_final */ size += sizeof(Elf64_Nhdr); - /* PT_LOAD type program header for kernel text region */ - size += sizeof(Elf64_Phdr); /* PT_LOADS */ - size += mem_chunk_cnt * sizeof(Elf64_Phdr); + size += phdr_count * sizeof(Elf64_Phdr); return size; } @@ -595,8 +597,8 @@ static size_t get_elfcorehdr_size(int mem_chunk_cnt) int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) { Elf64_Phdr *phdr_notes, *phdr_loads, *phdr_text; + int mem_chunk_cnt, phdr_text_cnt; size_t alloc_size; - int mem_chunk_cnt; void *ptr, *hdr; u64 hdr_off; @@ -615,12 +617,14 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) } mem_chunk_cnt = get_mem_chunk_cnt(); + phdr_text_cnt = os_info_has_vm() ? 1 : 0; - alloc_size = get_elfcorehdr_size(mem_chunk_cnt); + alloc_size = get_elfcorehdr_size(mem_chunk_cnt + phdr_text_cnt); hdr = kzalloc(alloc_size, GFP_KERNEL); - /* Without elfcorehdr /proc/vmcore cannot be created. Thus creating + /* + * Without elfcorehdr /proc/vmcore cannot be created. Thus creating * a dump with this crash kernel will fail. Panic now to allow other * dump mechanisms to take over. */ @@ -628,21 +632,23 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) panic("s390 kdump allocating elfcorehdr failed"); /* Init elf header */ - ptr = ehdr_init(hdr, mem_chunk_cnt); + phdr_notes = ehdr_init(hdr, mem_chunk_cnt + phdr_text_cnt); /* Init program headers */ - phdr_notes = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr)); - phdr_text = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr)); - phdr_loads = ptr; - ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * mem_chunk_cnt); + if (phdr_text_cnt) { + phdr_text = phdr_notes + 1; + phdr_loads = phdr_text + 1; + } else { + phdr_loads = phdr_notes + 1; + } + ptr = PTR_ADD(phdr_loads, sizeof(Elf64_Phdr) * mem_chunk_cnt); /* Init notes */ hdr_off = PTR_DIFF(ptr, hdr); ptr = notes_init(phdr_notes, ptr, ((unsigned long) hdr) + hdr_off); /* Init kernel text program header */ - text_init(phdr_text); + if (phdr_text_cnt) + text_init(phdr_text); /* Init loads */ - loads_init(phdr_loads); + loads_init(phdr_loads, phdr_text_cnt); /* Finalize program headers */ hdr_off = PTR_DIFF(ptr, hdr); *addr = (unsigned long long) hdr; diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index ece45b3f6f20..f8ca74e7678f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -2154,6 +2154,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, void *insn, int insn_len); +void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg); void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva); void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, u64 addr, unsigned long roots); diff --git a/arch/x86/include/asm/vmxfeatures.h b/arch/x86/include/asm/vmxfeatures.h index 266daf5b5b84..695f36664889 100644 --- a/arch/x86/include/asm/vmxfeatures.h +++ b/arch/x86/include/asm/vmxfeatures.h @@ -77,7 +77,7 @@ #define VMX_FEATURE_ENCLS_EXITING ( 2*32+ 15) /* "" VM-Exit on ENCLS (leaf dependent) */ #define VMX_FEATURE_RDSEED_EXITING ( 2*32+ 16) /* "" VM-Exit on RDSEED */ #define VMX_FEATURE_PAGE_MOD_LOGGING ( 2*32+ 17) /* "pml" Log dirty pages into buffer */ -#define VMX_FEATURE_EPT_VIOLATION_VE ( 2*32+ 18) /* "" Conditionally reflect EPT violations as #VE exceptions */ +#define VMX_FEATURE_EPT_VIOLATION_VE ( 2*32+ 18) /* Conditionally reflect EPT violations as #VE exceptions */ #define VMX_FEATURE_PT_CONCEAL_VMX ( 2*32+ 19) /* "" Suppress VMX indicators in Processor Trace */ #define VMX_FEATURE_XSAVES ( 2*32+ 20) /* "" Enable XSAVES and XRSTORS in guest */ #define VMX_FEATURE_MODE_BASED_EPT_EXEC ( 2*32+ 22) /* "ept_mode_based_exec" Enable separate EPT EXEC bits for supervisor vs. user */ diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 3cf156f70859..027a8c7a2c9e 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -215,7 +215,14 @@ out: int amd_smn_read(u16 node, u32 address, u32 *value) { - return __amd_smn_rw(node, address, value, false); + int err = __amd_smn_rw(node, address, value, false); + + if (PCI_POSSIBLE_ERROR(*value)) { + err = -ENODEV; + *value = 0; + } + + return err; } EXPORT_SYMBOL_GPL(amd_smn_read); diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index b180d8e497c3..cc0f7f70b17b 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -295,8 +295,15 @@ void machine_kexec_cleanup(struct kimage *image) void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; - void *control_page; + unsigned int host_mem_enc_active; int save_ftrace_enabled; + void *control_page; + + /* + * This must be done before load_segments() since if call depth tracking + * is used then GS must be valid to make any function calls. + */ + host_mem_enc_active = cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT); #ifdef CONFIG_KEXEC_JUMP if (image->preserve_context) @@ -358,7 +365,7 @@ void machine_kexec(struct kimage *image) (unsigned long)page_list, image->start, image->preserve_context, - cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)); + host_mem_enc_active); #ifdef CONFIG_KEXEC_JUMP if (image->preserve_context) diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index d64fb2b3eb69..fec95a770270 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -44,6 +44,7 @@ config KVM select KVM_VFIO select HAVE_KVM_PM_NOTIFIER if PM select KVM_GENERIC_HARDWARE_ENABLING + select KVM_WERROR if WERROR help Support hosting fully virtualized guest machines using hardware virtualization extensions. You will need a fairly recent @@ -66,7 +67,7 @@ config KVM_WERROR # FRAME_WARN, i.e. KVM_WERROR=y with KASAN=y requires special tuning. # Building KVM with -Werror and KASAN is still doable via enabling # the kernel-wide WERROR=y. - depends on KVM && EXPERT && !KASAN + depends on KVM && ((EXPERT && !KASAN) || WERROR) help Add -Werror to the build flags for KVM. @@ -97,15 +98,17 @@ config KVM_INTEL config KVM_INTEL_PROVE_VE bool "Check that guests do not receive #VE exceptions" - default KVM_PROVE_MMU || DEBUG_KERNEL - depends on KVM_INTEL + depends on KVM_INTEL && EXPERT help - Checks that KVM's page table management code will not incorrectly let guests receive a virtualization exception. Virtualization exceptions will be trapped by the hypervisor rather than injected in the guest. + Note: some CPUs appear to generate spurious EPT Violations #VEs + that trigger KVM's WARN, in particular with eptad=0 and/or nested + virtualization. + If unsure, say N. config X86_SGX_KVM diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index ebf41023be38..acd7d48100a1 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -59,7 +59,17 @@ #define MAX_APIC_VECTOR 256 #define APIC_VECTORS_PER_REG 32 -static bool lapic_timer_advance_dynamic __read_mostly; +/* + * Enable local APIC timer advancement (tscdeadline mode only) with adaptive + * tuning. When enabled, KVM programs the host timer event to fire early, i.e. + * before the deadline expires, to account for the delay between taking the + * VM-Exit (to inject the guest event) and the subsequent VM-Enter to resume + * the guest, i.e. so that the interrupt arrives in the guest with minimal + * latency relative to the deadline programmed by the guest. + */ +static bool lapic_timer_advance __read_mostly = true; +module_param(lapic_timer_advance, bool, 0444); + #define LAPIC_TIMER_ADVANCE_ADJUST_MIN 100 /* clock cycles */ #define LAPIC_TIMER_ADVANCE_ADJUST_MAX 10000 /* clock cycles */ #define LAPIC_TIMER_ADVANCE_NS_INIT 1000 @@ -1854,16 +1864,14 @@ static void __kvm_wait_lapic_expire(struct kvm_vcpu *vcpu) guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); trace_kvm_wait_lapic_expire(vcpu->vcpu_id, guest_tsc - tsc_deadline); - if (lapic_timer_advance_dynamic) { - adjust_lapic_timer_advance(vcpu, guest_tsc - tsc_deadline); - /* - * If the timer fired early, reread the TSC to account for the - * overhead of the above adjustment to avoid waiting longer - * than is necessary. - */ - if (guest_tsc < tsc_deadline) - guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); - } + adjust_lapic_timer_advance(vcpu, guest_tsc - tsc_deadline); + + /* + * If the timer fired early, reread the TSC to account for the overhead + * of the above adjustment to avoid waiting longer than is necessary. + */ + if (guest_tsc < tsc_deadline) + guest_tsc = kvm_read_l1_tsc(vcpu, rdtsc()); if (guest_tsc < tsc_deadline) __wait_lapic_expire(vcpu, tsc_deadline - guest_tsc); @@ -2812,7 +2820,7 @@ static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) return HRTIMER_NORESTART; } -int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) +int kvm_create_lapic(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic; @@ -2845,13 +2853,8 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD); apic->lapic_timer.timer.function = apic_timer_fn; - if (timer_advance_ns == -1) { + if (lapic_timer_advance) apic->lapic_timer.timer_advance_ns = LAPIC_TIMER_ADVANCE_NS_INIT; - lapic_timer_advance_dynamic = true; - } else { - apic->lapic_timer.timer_advance_ns = timer_advance_ns; - lapic_timer_advance_dynamic = false; - } /* * Stuff the APIC ENABLE bit in lieu of temporarily incrementing diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 0a0ea4b5dd8c..a69e706b9080 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -85,7 +85,7 @@ struct kvm_lapic { struct dest_map; -int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns); +int kvm_create_lapic(struct kvm_vcpu *vcpu); void kvm_free_lapic(struct kvm_vcpu *vcpu); int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 662f62dfb2aa..8d74bdef68c1 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -336,16 +336,19 @@ static int is_cpuid_PSE36(void) #ifdef CONFIG_X86_64 static void __set_spte(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); WRITE_ONCE(*sptep, spte); } static void __update_clear_spte_fast(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); WRITE_ONCE(*sptep, spte); } static u64 __update_clear_spte_slow(u64 *sptep, u64 spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(spte)); return xchg(sptep, spte); } @@ -4101,23 +4104,31 @@ static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, int *root_level return leaf; } -/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */ -static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) +static int get_sptes_lockless(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, + int *root_level) { - u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; - struct rsvd_bits_validate *rsvd_check; - int root, leaf, level; - bool reserved = false; + int leaf; walk_shadow_page_lockless_begin(vcpu); if (is_tdp_mmu_active(vcpu)) - leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root); + leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, root_level); else - leaf = get_walk(vcpu, addr, sptes, &root); + leaf = get_walk(vcpu, addr, sptes, root_level); walk_shadow_page_lockless_end(vcpu); + return leaf; +} + +/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */ +static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) +{ + u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; + struct rsvd_bits_validate *rsvd_check; + int root, leaf, level; + bool reserved = false; + leaf = get_sptes_lockless(vcpu, addr, sptes, &root); if (unlikely(leaf < 0)) { *sptep = 0ull; return reserved; @@ -4400,9 +4411,6 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, return RET_PF_EMULATE; } - fault->mmu_seq = vcpu->kvm->mmu_invalidate_seq; - smp_rmb(); - /* * Check for a relevant mmu_notifier invalidation event before getting * the pfn from the primary MMU, and before acquiring mmu_lock. @@ -5921,6 +5929,22 @@ emulate: } EXPORT_SYMBOL_GPL(kvm_mmu_page_fault); +void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg) +{ + u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; + int root_level, leaf, level; + + leaf = get_sptes_lockless(vcpu, gpa, sptes, &root_level); + if (unlikely(leaf < 0)) + return; + + pr_err("%s %llx", msg, gpa); + for (level = root_level; level >= leaf; level--) + pr_cont(", spte[%d] = 0x%llx", level, sptes[level]); + pr_cont("\n"); +} +EXPORT_SYMBOL_GPL(kvm_mmu_print_sptes); + static void __kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, u64 addr, hpa_t root_hpa) { diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 5dd5405fa07a..52fa004a1fbc 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -3,6 +3,8 @@ #ifndef KVM_X86_MMU_SPTE_H #define KVM_X86_MMU_SPTE_H +#include <asm/vmx.h> + #include "mmu.h" #include "mmu_internal.h" @@ -276,6 +278,13 @@ static inline bool is_shadow_present_pte(u64 pte) return !!(pte & SPTE_MMU_PRESENT_MASK); } +static inline bool is_ept_ve_possible(u64 spte) +{ + return (shadow_present_mask & VMX_EPT_SUPPRESS_VE_BIT) && + !(spte & VMX_EPT_SUPPRESS_VE_BIT) && + (spte & VMX_EPT_RWX_MASK) != VMX_EPT_MISCONFIG_WX_VALUE; +} + /* * Returns true if A/D bits are supported in hardware and are enabled by KVM. * When enabled, KVM uses A/D bits for all non-nested MMUs. Because L1 can diff --git a/arch/x86/kvm/mmu/tdp_iter.h b/arch/x86/kvm/mmu/tdp_iter.h index fae559559a80..2880fd392e0c 100644 --- a/arch/x86/kvm/mmu/tdp_iter.h +++ b/arch/x86/kvm/mmu/tdp_iter.h @@ -21,11 +21,13 @@ static inline u64 kvm_tdp_mmu_read_spte(tdp_ptep_t sptep) static inline u64 kvm_tdp_mmu_write_spte_atomic(tdp_ptep_t sptep, u64 new_spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(new_spte)); return xchg(rcu_dereference(sptep), new_spte); } static inline void __kvm_tdp_mmu_write_spte(tdp_ptep_t sptep, u64 new_spte) { + KVM_MMU_WARN_ON(is_ept_ve_possible(new_spte)); WRITE_ONCE(*rcu_dereference(sptep), new_spte); } diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 1259dd63defc..36539c1b36cd 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -626,7 +626,7 @@ static inline int tdp_mmu_zap_spte_atomic(struct kvm *kvm, * SPTEs. */ handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte, - 0, iter->level, true); + SHADOW_NONPRESENT_VALUE, iter->level, true); return 0; } diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0623cfaa7bb0..95095a233a45 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -779,6 +779,14 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, */ fpstate_set_confidential(&vcpu->arch.guest_fpu); vcpu->arch.guest_state_protected = true; + + /* + * SEV-ES guest mandates LBR Virtualization to be _always_ ON. Enable it + * only after setting guest_state_protected because KVM_SET_MSRS allows + * dynamic toggling of LBRV (for performance reason) on write access to + * MSR_IA32_DEBUGCTLMSR when guest_state_protected is not set. + */ + svm_enable_lbrv(vcpu); return 0; } @@ -2406,6 +2414,12 @@ void __init sev_hardware_setup(void) if (!boot_cpu_has(X86_FEATURE_SEV_ES)) goto out; + if (!lbrv) { + WARN_ONCE(!boot_cpu_has(X86_FEATURE_LBRV), + "LBRV must be present for SEV-ES support"); + goto out; + } + /* Has the system been allocated ASIDs for SEV-ES? */ if (min_sev_asid == 1) goto out; @@ -3216,7 +3230,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) struct kvm_vcpu *vcpu = &svm->vcpu; svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; - svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; /* * An SEV-ES guest requires a VMSA area that is a separate from the @@ -3268,10 +3281,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) /* Clear intercepts on selected MSRs */ set_msr_interception(vcpu, svm->msrpm, MSR_EFER, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_CR_PAT, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); } void sev_init_vmcb(struct vcpu_svm *svm) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index c8dc25886c16..296c524988f9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -99,6 +99,7 @@ static const struct svm_direct_access_msrs { { .index = MSR_IA32_SPEC_CTRL, .always = false }, { .index = MSR_IA32_PRED_CMD, .always = false }, { .index = MSR_IA32_FLUSH_CMD, .always = false }, + { .index = MSR_IA32_DEBUGCTLMSR, .always = false }, { .index = MSR_IA32_LASTBRANCHFROMIP, .always = false }, { .index = MSR_IA32_LASTBRANCHTOIP, .always = false }, { .index = MSR_IA32_LASTINTFROMIP, .always = false }, @@ -215,7 +216,7 @@ int vgif = true; module_param(vgif, int, 0444); /* enable/disable LBR virtualization */ -static int lbrv = true; +int lbrv = true; module_param(lbrv, int, 0444); static int tsc_scaling = true; @@ -990,7 +991,7 @@ void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb) vmcb_mark_dirty(to_vmcb, VMCB_LBR); } -static void svm_enable_lbrv(struct kvm_vcpu *vcpu) +void svm_enable_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1000,6 +1001,9 @@ static void svm_enable_lbrv(struct kvm_vcpu *vcpu) set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); + if (sev_es_guest(vcpu->kvm)) + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_DEBUGCTLMSR, 1, 1); + /* Move the LBR msrs to the vmcb02 so that the guest can see them. */ if (is_guest_mode(vcpu)) svm_copy_lbrs(svm->vmcb, svm->vmcb01.ptr); @@ -1009,6 +1013,8 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm); + svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 0, 0); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0); @@ -2822,10 +2828,24 @@ static int svm_get_msr_feature(struct kvm_msr_entry *msr) return 0; } +static bool +sev_es_prevent_msr_access(struct kvm_vcpu *vcpu, struct msr_data *msr_info) +{ + return sev_es_guest(vcpu->kvm) && + vcpu->arch.guest_state_protected && + svm_msrpm_offset(msr_info->index) != MSR_INVALID && + !msr_write_intercepted(vcpu, msr_info->index); +} + static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { struct vcpu_svm *svm = to_svm(vcpu); + if (sev_es_prevent_msr_access(vcpu, msr_info)) { + msr_info->data = 0; + return -EINVAL; + } + switch (msr_info->index) { case MSR_AMD64_TSC_RATIO: if (!msr_info->host_initiated && @@ -2976,6 +2996,10 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) u32 ecx = msr->index; u64 data = msr->data; + + if (sev_es_prevent_msr_access(vcpu, msr)) + return -EINVAL; + switch (ecx) { case MSR_AMD64_TSC_RATIO: @@ -3846,16 +3870,27 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); /* - * KVM should never request an NMI window when vNMI is enabled, as KVM - * allows at most one to-be-injected NMI and one pending NMI, i.e. if - * two NMIs arrive simultaneously, KVM will inject one and set - * V_NMI_PENDING for the other. WARN, but continue with the standard - * single-step approach to try and salvage the pending NMI. + * If NMIs are outright masked, i.e. the vCPU is already handling an + * NMI, and KVM has not yet intercepted an IRET, then there is nothing + * more to do at this time as KVM has already enabled IRET intercepts. + * If KVM has already intercepted IRET, then single-step over the IRET, + * as NMIs aren't architecturally unmasked until the IRET completes. + * + * If vNMI is enabled, KVM should never request an NMI window if NMIs + * are masked, as KVM allows at most one to-be-injected NMI and one + * pending NMI. If two NMIs arrive simultaneously, KVM will inject one + * NMI and set V_NMI_PENDING for the other, but if and only if NMIs are + * unmasked. KVM _will_ request an NMI window in some situations, e.g. + * if the vCPU is in an STI shadow or if GIF=0, KVM can't immediately + * inject the NMI. In those situations, KVM needs to single-step over + * the STI shadow or intercept STGI. */ - WARN_ON_ONCE(is_vnmi_enabled(svm)); + if (svm_get_nmi_mask(vcpu)) { + WARN_ON_ONCE(is_vnmi_enabled(svm)); - if (svm_get_nmi_mask(vcpu) && !svm->awaiting_iret_completion) - return; /* IRET will cause a vm exit */ + if (!svm->awaiting_iret_completion) + return; /* IRET will cause a vm exit */ + } /* * SEV-ES guests are responsible for signaling when a vCPU is ready to @@ -5265,6 +5300,12 @@ static __init int svm_hardware_setup(void) nrips = nrips && boot_cpu_has(X86_FEATURE_NRIPS); + if (lbrv) { + if (!boot_cpu_has(X86_FEATURE_LBRV)) + lbrv = false; + else + pr_info("LBR virtualization supported\n"); + } /* * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which * may be modified by svm_adjust_mmio_mask()), as well as nrips. @@ -5318,14 +5359,6 @@ static __init int svm_hardware_setup(void) svm_x86_ops.set_vnmi_pending = NULL; } - - if (lbrv) { - if (!boot_cpu_has(X86_FEATURE_LBRV)) - lbrv = false; - else - pr_info("LBR virtualization supported\n"); - } - if (!enable_pmu) pr_info("PMU virtualization is disabled\n"); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index be57213cd295..0f1472690b59 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -30,7 +30,7 @@ #define IOPM_SIZE PAGE_SIZE * 3 #define MSRPM_SIZE PAGE_SIZE * 2 -#define MAX_DIRECT_ACCESS_MSRS 47 +#define MAX_DIRECT_ACCESS_MSRS 48 #define MSRPM_OFFSETS 32 extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; extern bool npt_enabled; @@ -39,6 +39,7 @@ extern int vgif; extern bool intercept_smi; extern bool x2avic_enabled; extern bool vnmi; +extern int lbrv; /* * Clean bits in VMCB. @@ -552,6 +553,7 @@ u32 *svm_vcpu_alloc_msrpm(void); void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm); void svm_vcpu_free_msrpm(u32 *msrpm); void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb); +void svm_enable_lbrv(struct kvm_vcpu *vcpu); void svm_update_lbrv(struct kvm_vcpu *vcpu); int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer); diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index d5b832126e34..643935a0f70a 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2242,6 +2242,9 @@ static void prepare_vmcs02_constant_state(struct vcpu_vmx *vmx) vmcs_write64(EPT_POINTER, construct_eptp(&vmx->vcpu, 0, PT64_ROOT_4LEVEL)); + if (vmx->ve_info) + vmcs_write64(VE_INFORMATION_ADDRESS, __pa(vmx->ve_info)); + /* All VMFUNCs are currently emulated through L0 vmexits. */ if (cpu_has_vmx_vmfunc()) vmcs_write64(VM_FUNCTION_CONTROL, 0); @@ -6230,6 +6233,8 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu, else if (is_alignment_check(intr_info) && !vmx_guest_inject_ac(vcpu)) return true; + else if (is_ve_fault(intr_info)) + return true; return false; case EXIT_REASON_EXTERNAL_INTERRUPT: return true; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 6051fad5945f..b3c83c06f826 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5218,8 +5218,15 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) if (is_invalid_opcode(intr_info)) return handle_ud(vcpu); - if (KVM_BUG_ON(is_ve_fault(intr_info), vcpu->kvm)) - return -EIO; + if (WARN_ON_ONCE(is_ve_fault(intr_info))) { + struct vmx_ve_information *ve_info = vmx->ve_info; + + WARN_ONCE(ve_info->exit_reason != EXIT_REASON_EPT_VIOLATION, + "Unexpected #VE on VM-Exit reason 0x%x", ve_info->exit_reason); + dump_vmcs(vcpu); + kvm_mmu_print_sptes(vcpu, ve_info->guest_physical_address, "#VE"); + return 1; + } error_code = 0; if (intr_info & INTR_INFO_DELIVER_CODE_MASK) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 082ac6d95a3a..8c9e4281d978 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -164,15 +164,6 @@ module_param(kvmclock_periodic_sync, bool, 0444); static u32 __read_mostly tsc_tolerance_ppm = 250; module_param(tsc_tolerance_ppm, uint, 0644); -/* - * lapic timer advance (tscdeadline mode only) in nanoseconds. '-1' enables - * adaptive tuning starting from default advancement of 1000ns. '0' disables - * advancement entirely. Any other value is used as-is and disables adaptive - * tuning, i.e. allows privileged userspace to set an exact advancement time. - */ -static int __read_mostly lapic_timer_advance_ns = -1; -module_param(lapic_timer_advance_ns, int, 0644); - static bool __read_mostly vector_hashing = true; module_param(vector_hashing, bool, 0444); @@ -12169,7 +12160,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) if (r < 0) return r; - r = kvm_create_lapic(vcpu, lapic_timer_advance_ns); + r = kvm_create_lapic(vcpu); if (r < 0) goto fail_mmu_destroy; diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 2d4a35e6dd18..09a87fa222c7 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -145,7 +145,7 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) dev_name(&adev->dev), event, (u32) ac->state); acpi_notifier_call_chain(adev, event, (u32) ac->state); - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(ac->charger); } } @@ -268,7 +268,7 @@ static int acpi_ac_resume(struct device *dev) if (acpi_ac_get_state(ac)) return 0; if (old_state != ac->state) - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(ac->charger); return 0; } diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 9515bcfe5e97..73903a497d73 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -909,7 +909,7 @@ static void __exit einj_exit(void) if (einj_initialized) platform_driver_unregister(&einj_driver); - platform_device_del(einj_dev); + platform_device_unregister(einj_dev); } module_init(einj_init); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e7793ee9e649..68dd17f96f63 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1333,10 +1333,13 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (ec->busy_polling || bits > 8) acpi_ec_burst_enable(ec); - for (i = 0; i < bytes; ++i, ++address, ++value) + for (i = 0; i < bytes; ++i, ++address, ++value) { result = (function == ACPI_READ) ? acpi_ec_read(ec, address, value) : acpi_ec_write(ec, address, *value); + if (result < 0) + break; + } if (ec->busy_polling || bits > 8) acpi_ec_burst_disable(ec); @@ -1348,8 +1351,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, return AE_NOT_FOUND; case -ETIME: return AE_TIME; - default: + case 0: return AE_OK; + default: + return AE_ERROR; } } diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 94e3c000df2e..dc8164b182dc 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -610,7 +610,7 @@ static void acpi_sbs_callback(void *context) if (sbs->charger_exists) { acpi_ac_get_present(sbs); if (sbs->charger_present != saved_charger_state) - kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(sbs->charger); } if (sbs->manager_present) { @@ -622,7 +622,7 @@ static void acpi_sbs_callback(void *context) acpi_battery_read(bat); if (saved_battery_state == bat->present) continue; - kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE); + power_supply_changed(bat->bat); } } } diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 202234ba54bd..ae9384282273 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -277,15 +277,25 @@ acpi_evaluate_integer(acpi_handle handle, EXPORT_SYMBOL(acpi_evaluate_integer); -int acpi_get_local_address(acpi_handle handle, u32 *addr) +int acpi_get_local_u64_address(acpi_handle handle, u64 *addr) { - unsigned long long adr; acpi_status status; - status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, addr); if (ACPI_FAILURE(status)) return -ENODATA; + return 0; +} +EXPORT_SYMBOL(acpi_get_local_u64_address); + +int acpi_get_local_address(acpi_handle handle, u32 *addr) +{ + u64 adr; + int ret; + ret = acpi_get_local_u64_address(handle, &adr); + if (ret < 0) + return ret; *addr = (u32)adr; return 0; } diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 817838e2f70e..3cb455a32d92 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -915,10 +915,13 @@ static const struct scsi_host_template pata_macio_sht = { .sg_tablesize = MAX_DCMDS, /* We may not need that strict one */ .dma_boundary = ATA_DMA_BOUNDARY, - /* Not sure what the real max is but we know it's less than 64K, let's - * use 64K minus 256 + /* + * The SCSI core requires the segment size to cover at least a page, so + * for 64K page size kernels this must be at least 64K. However the + * hardware can't handle 64K, so pata_macio_qc_prep() will split large + * requests. */ - .max_segment_size = MAX_DBDMA_SEG, + .max_segment_size = SZ_64K, .device_configure = pata_macio_device_configure, .sdev_groups = ata_common_sdev_groups, .can_queue = ATA_DEF_QUEUE, diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 631dca2e4e84..75f189e42f88 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1824,8 +1824,8 @@ static int null_validate_conf(struct nullb_device *dev) dev->queue_mode = NULL_Q_MQ; } - dev->blocksize = round_down(dev->blocksize, 512); - dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096); + if (blk_validate_block_size(dev->blocksize)) + return -EINVAL; if (dev->use_per_node_hctx) { if (dev->submit_queues != nr_online_nodes) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 6b8b9956ba69..7bb87fa5f7a1 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -28,7 +28,7 @@ #include <linux/tpm_eventlog.h> #ifdef CONFIG_X86 -#include <asm/intel-family.h> +#include <asm/cpu_device_id.h> #endif #define TPM_MINOR 224 /* officially assigned */ diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 176cd8dbf1db..fdef214b9f6b 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -1020,7 +1020,8 @@ void tpm_tis_remove(struct tpm_chip *chip) interrupt = 0; tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); - flush_work(&priv->free_irq_work); + if (priv->free_irq_work.func) + flush_work(&priv->free_irq_work); tpm_tis_clkrun_enable(chip, false); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 13e99cf65efe..690ad8e9b731 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -210,7 +210,7 @@ static inline int tpm_tis_verify_crc(struct tpm_tis_data *data, size_t len, static inline bool is_bsw(void) { #ifdef CONFIG_X86 - return ((boot_cpu_data.x86_model == INTEL_FAM6_ATOM_AIRMONT) ? 1 : 0); + return (boot_cpu_data.x86_vfm == INTEL_ATOM_AIRMONT) ? 1 : 0; #else return false; #endif diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c index 25b8e1a80ddc..b32a59fe55e7 100644 --- a/drivers/clk/sifive/sifive-prci.c +++ b/drivers/clk/sifive/sifive-prci.c @@ -4,7 +4,6 @@ * Copyright (C) 2020 Zong Li */ -#include <linux/clkdev.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> @@ -537,13 +536,6 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd, return r; } - r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); - if (r) { - dev_warn(dev, "Failed to register clkdev for %s: %d\n", - init.name, r); - return r; - } - pd->hw_clks.hws[i] = &pic->hw; } diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index f04ae67dda37..fc275d41d51e 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -26,10 +26,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/fs.h> -#include <linux/amd-pstate.h> #include <acpi/cppc_acpi.h> +#include "amd-pstate.h" + /* * Abbreviations: * amd_pstate_ut: used as a shortform for AMD P-State unit test. diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 1b7e82a0ad2e..9ad62dbe8bfb 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -36,7 +36,6 @@ #include <linux/delay.h> #include <linux/uaccess.h> #include <linux/static_call.h> -#include <linux/amd-pstate.h> #include <linux/topology.h> #include <acpi/processor.h> @@ -46,6 +45,8 @@ #include <asm/processor.h> #include <asm/cpufeature.h> #include <asm/cpu_device_id.h> + +#include "amd-pstate.h" #include "amd-pstate-trace.h" #define AMD_PSTATE_TRANSITION_LATENCY 20000 @@ -53,6 +54,37 @@ #define CPPC_HIGHEST_PERF_PERFORMANCE 196 #define CPPC_HIGHEST_PERF_DEFAULT 166 +#define AMD_CPPC_EPP_PERFORMANCE 0x00 +#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80 +#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF +#define AMD_CPPC_EPP_POWERSAVE 0xFF + +/* + * enum amd_pstate_mode - driver working mode of amd pstate + */ +enum amd_pstate_mode { + AMD_PSTATE_UNDEFINED = 0, + AMD_PSTATE_DISABLE, + AMD_PSTATE_PASSIVE, + AMD_PSTATE_ACTIVE, + AMD_PSTATE_GUIDED, + AMD_PSTATE_MAX, +}; + +static const char * const amd_pstate_mode_string[] = { + [AMD_PSTATE_UNDEFINED] = "undefined", + [AMD_PSTATE_DISABLE] = "disable", + [AMD_PSTATE_PASSIVE] = "passive", + [AMD_PSTATE_ACTIVE] = "active", + [AMD_PSTATE_GUIDED] = "guided", + NULL, +}; + +struct quirk_entry { + u32 nominal_freq; + u32 lowest_freq; +}; + /* * TODO: We need more time to fine tune processors with shared memory solution * with community together. @@ -669,7 +701,7 @@ static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) if (state) policy->cpuinfo.max_freq = cpudata->max_freq; else - policy->cpuinfo.max_freq = cpudata->nominal_freq; + policy->cpuinfo.max_freq = cpudata->nominal_freq * 1000; policy->max = policy->cpuinfo.max_freq; diff --git a/include/linux/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index d58fc022ec46..e6a28e7f4dbf 100644 --- a/include/linux/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * linux/include/linux/amd-pstate.h - * * Copyright (C) 2022 Advanced Micro Devices, Inc. * * Author: Meng Li <li.meng@amd.com> @@ -12,11 +10,6 @@ #include <linux/pm_qos.h> -#define AMD_CPPC_EPP_PERFORMANCE 0x00 -#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80 -#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF -#define AMD_CPPC_EPP_POWERSAVE 0xFF - /********************************************************************* * AMD P-state INTERFACE * *********************************************************************/ @@ -108,30 +101,4 @@ struct amd_cpudata { bool suspended; }; -/* - * enum amd_pstate_mode - driver working mode of amd pstate - */ -enum amd_pstate_mode { - AMD_PSTATE_UNDEFINED = 0, - AMD_PSTATE_DISABLE, - AMD_PSTATE_PASSIVE, - AMD_PSTATE_ACTIVE, - AMD_PSTATE_GUIDED, - AMD_PSTATE_MAX, -}; - -static const char * const amd_pstate_mode_string[] = { - [AMD_PSTATE_UNDEFINED] = "undefined", - [AMD_PSTATE_DISABLE] = "disable", - [AMD_PSTATE_PASSIVE] = "passive", - [AMD_PSTATE_ACTIVE] = "active", - [AMD_PSTATE_GUIDED] = "guided", - NULL, -}; - -struct quirk_entry { - u32 nominal_freq; - u32 lowest_freq; -}; - #endif /* _LINUX_AMD_PSTATE_H */ diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 4b986c044741..65d3f79104bd 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1153,7 +1153,8 @@ static void intel_pstate_update_policies(void) static void __intel_pstate_update_max_freq(struct cpudata *cpudata, struct cpufreq_policy *policy) { - intel_pstate_get_hwp_cap(cpudata); + if (hwp_active) + intel_pstate_get_hwp_cap(cpudata); policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 00a9f0eef8dd..3c2b6144be23 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2352,15 +2352,6 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, struct device *dev; int rc; - switch (mode) { - case CXL_DECODER_RAM: - case CXL_DECODER_PMEM: - break; - default: - dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode); - return ERR_PTR(-EINVAL); - } - cxlr = cxl_region_alloc(cxlrd, id); if (IS_ERR(cxlr)) return cxlr; @@ -2415,6 +2406,15 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, { int rc; + switch (mode) { + case CXL_DECODER_RAM: + case CXL_DECODER_PMEM: + break; + default: + dev_err(&cxlrd->cxlsd.cxld.dev, "unsupported mode %d\n", mode); + return ERR_PTR(-EINVAL); + } + rc = memregion_alloc(GFP_KERNEL); if (rc < 0) return ERR_PTR(rc); diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 1f3520d76861..a17f3c0cdfa6 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -81,7 +81,7 @@ int __amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset, amd64_warn("%s: error reading F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); - return err; + return pcibios_err_to_errno(err); } int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, @@ -94,7 +94,7 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, amd64_warn("%s: error writing to F%dx%03x.\n", func, PCI_FUNC(pdev->devfn), offset); - return err; + return pcibios_err_to_errno(err); } /* @@ -1025,8 +1025,10 @@ static int gpu_get_node_map(struct amd64_pvt *pvt) } ret = pci_read_config_dword(pdev, REG_LOCAL_NODE_TYPE_MAP, &tmp); - if (ret) + if (ret) { + ret = pcibios_err_to_errno(ret); goto out; + } gpu_node_map.node_count = FIELD_GET(LNTM_NODE_COUNT, tmp); gpu_node_map.base_node_id = FIELD_GET(LNTM_BASE_NODE_ID, tmp); diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index cdd8480e7368..dbe9fe5f2ca6 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -800,7 +800,7 @@ static int errcmd_enable_error_reporting(bool enable) rc = pci_read_config_word(imc->pdev, ERRCMD_OFFSET, &errcmd); if (rc) - return rc; + return pcibios_err_to_errno(rc); if (enable) errcmd |= ERRCMD_CE | ERRSTS_UE; @@ -809,7 +809,7 @@ static int errcmd_enable_error_reporting(bool enable) rc = pci_write_config_word(imc->pdev, ERRCMD_OFFSET, errcmd); if (rc) - return rc; + return pcibios_err_to_errno(rc); return 0; } diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 8a347b938406..a7b56eff086f 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -12,6 +12,7 @@ #include <linux/ctype.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/seq_file.h> @@ -796,6 +797,9 @@ int cs_dsp_coeff_write_ctrl(struct cs_dsp_coeff_ctl *ctl, lockdep_assert_held(&ctl->dsp->pwr_lock); + if (ctl->flags && !(ctl->flags & WMFW_CTL_FLAG_WRITEABLE)) + return -EPERM; + if (len + off * sizeof(u32) > ctl->len) return -EINVAL; @@ -1047,7 +1051,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp, ctl->fw_name = dsp->fw_name; ctl->alg_region = *alg_region; - if (subname && dsp->fw_ver >= 2) { + if (subname && dsp->wmfw_ver >= 2) { ctl->subname_len = subname_len; ctl->subname = kasprintf(GFP_KERNEL, "%.*s", subname_len, subname); if (!ctl->subname) { @@ -1174,7 +1178,7 @@ static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, raw = (const struct wmfw_adsp_alg_data *)region->data; - switch (dsp->fw_ver) { + switch (dsp->wmfw_ver) { case 0: case 1: if (sizeof(*raw) > data_len) @@ -1251,7 +1255,7 @@ static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, blk->offset = le16_to_cpu(raw->hdr.offset); blk->mem_type = le16_to_cpu(raw->hdr.type); - switch (dsp->fw_ver) { + switch (dsp->wmfw_ver) { case 0: case 1: if (sizeof(*raw) > (data_len - pos)) @@ -1470,7 +1474,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, const struct wmfw_region *region; const struct cs_dsp_region *mem; const char *region_name; - char *text = NULL; struct cs_dsp_buf *buf; unsigned int reg; int regions = 0; @@ -1499,8 +1502,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, goto out_fw; } - cs_dsp_info(dsp, "Firmware version: %d\n", header->ver); - dsp->fw_ver = header->ver; + dsp->wmfw_ver = header->ver; if (header->core != dsp->type) { cs_dsp_err(dsp, "%s: invalid core %d != %d\n", @@ -1523,8 +1525,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, goto out_fw; } - cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, - le64_to_cpu(footer->timestamp)); + cs_dsp_info(dsp, "%s: format %d timestamp %#llx\n", file, header->ver, + le64_to_cpu(footer->timestamp)); while (pos < firmware->size) { /* Is there enough data for a complete block header? */ @@ -1542,15 +1544,15 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, region_name = "Unknown"; reg = 0; - text = NULL; offset = le32_to_cpu(region->offset) & 0xffffff; type = be32_to_cpu(region->type) & 0xff; switch (type) { + case WMFW_INFO_TEXT: case WMFW_NAME_TEXT: - region_name = "Firmware name"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); + region_name = "Info/Name"; + cs_dsp_info(dsp, "%s: %.*s\n", file, + min(le32_to_cpu(region->len), 100), region->data); break; case WMFW_ALGORITHM_DATA: region_name = "Algorithm"; @@ -1558,11 +1560,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, if (ret != 0) goto out_fw; break; - case WMFW_INFO_TEXT: - region_name = "Information"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); - break; case WMFW_ABSOLUTE: region_name = "Absolute"; reg = offset; @@ -1596,13 +1593,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, regions, le32_to_cpu(region->len), offset, region_name); - if (text) { - memcpy(text, region->data, le32_to_cpu(region->len)); - cs_dsp_info(dsp, "%s: %s\n", file, text); - kfree(text); - text = NULL; - } - if (reg) { buf = cs_dsp_buf_alloc(region->data, le32_to_cpu(region->len), @@ -1644,7 +1634,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); - kfree(text); if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); @@ -1793,7 +1782,7 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, list_add_tail(&alg_region->list, &dsp->alg_regions); - if (dsp->fw_ver > 0) + if (dsp->wmfw_ver > 0) cs_dsp_ctl_fixup_base(dsp, alg_region); return alg_region; @@ -1916,7 +1905,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp1_alg[i + 1].dm); len -= be32_to_cpu(adsp1_alg[i].dm); @@ -1938,7 +1927,7 @@ static int cs_dsp_adsp1_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp1_alg[i + 1].zm); len -= be32_to_cpu(adsp1_alg[i].zm); @@ -2029,7 +2018,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].xm); len -= be32_to_cpu(adsp2_alg[i].xm); @@ -2051,7 +2040,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].ym); len -= be32_to_cpu(adsp2_alg[i].ym); @@ -2073,7 +2062,7 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) ret = PTR_ERR(alg_region); goto out; } - if (dsp->fw_ver == 0) { + if (dsp->wmfw_ver == 0) { if (i + 1 < n_algs) { len = be32_to_cpu(adsp2_alg[i + 1].zm); len -= be32_to_cpu(adsp2_alg[i].zm); @@ -2177,7 +2166,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware struct cs_dsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg, version; - char *text = NULL; struct cs_dsp_buf *buf; if (!firmware) @@ -2246,7 +2234,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware region_name = "Unknown"; switch (type) { case (WMFW_NAME_TEXT << 8): - text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL); + cs_dsp_info(dsp, "%s: %.*s\n", dsp->fw_name, + min(le32_to_cpu(blk->len), 100), blk->data); break; case (WMFW_INFO_TEXT << 8): case (WMFW_METADATA << 8): @@ -2318,13 +2307,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware break; } - if (text) { - memcpy(text, blk->data, le32_to_cpu(blk->len)); - cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text); - kfree(text); - text = NULL; - } - if (reg) { buf = cs_dsp_buf_alloc(blk->data, le32_to_cpu(blk->len), @@ -2364,7 +2346,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware out_fw: regmap_async_complete(regmap); cs_dsp_buf_free(&buf_list); - kfree(text); if (ret == -EOVERFLOW) cs_dsp_err(dsp, "%s: file content overflows file data\n", file); @@ -2431,8 +2412,8 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp1_init, FW_CS_DSP); * Return: Zero for success, a negative number on error. */ int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name) { unsigned int val; @@ -2725,8 +2706,8 @@ static void cs_dsp_halo_stop_watchdog(struct cs_dsp *dsp) * Return: Zero for success, a negative number on error. */ int cs_dsp_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name) { int ret; diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 5b9dc26e6bcb..552c78f5f059 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -136,7 +136,7 @@ static int efi_pstore_read_func(struct pstore_record *record, &size, record->buf); if (status != EFI_SUCCESS) { kfree(record->buf); - return -EIO; + return efi_status_to_err(status); } /* @@ -189,7 +189,7 @@ static ssize_t efi_pstore_read(struct pstore_record *record) return 0; if (status != EFI_SUCCESS) - return -EIO; + return efi_status_to_err(status); /* skip variables that don't concern us */ if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID)) @@ -227,7 +227,7 @@ static int efi_pstore_write(struct pstore_record *record) record->size, record->psi->buf, true); efivar_unlock(); - return status == EFI_SUCCESS ? 0 : -EIO; + return efi_status_to_err(status); }; static int efi_pstore_erase(struct pstore_record *record) @@ -238,7 +238,7 @@ static int efi_pstore_erase(struct pstore_record *record) PSTORE_EFI_ATTRIBUTES, 0, NULL); if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) - return -EIO; + return efi_status_to_err(status); return 0; } diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 684c9354637c..d0ef93551c44 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -41,7 +41,7 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) unsigned long __weak kernel_entry_address(unsigned long kernel_addr, efi_loaded_image_t *image) { - return *(unsigned long *)(kernel_addr + 8) - VMLINUX_LOAD_ADDRESS + kernel_addr; + return *(unsigned long *)(kernel_addr + 8) - PHYSADDR(VMLINUX_LOAD_ADDRESS) + kernel_addr; } efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index ac8c0ef85158..af2c82f7bd90 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -41,6 +41,7 @@ SECTIONS } /DISCARD/ : { + *(.discard .discard.*) *(.modinfo .init.modinfo) } } diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 5d56bc40a79d..708b777857d3 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -213,7 +213,7 @@ extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); * Calls the appropriate efi_runtime_service() with the appropriate * arguments. */ -static void efi_call_rts(struct work_struct *work) +static void __nocfi efi_call_rts(struct work_struct *work) { const union efi_rts_args *args = efi_rts_work.args; efi_status_t status = EFI_NOT_FOUND; @@ -435,7 +435,7 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, return status; } -static efi_status_t +static efi_status_t __nocfi virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data) { @@ -469,7 +469,7 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, return status; } -static efi_status_t +static efi_status_t __nocfi virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space, u64 *remaining_space, u64 *max_variable_size) { @@ -499,10 +499,9 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) return status; } -static void virt_efi_reset_system(int reset_type, - efi_status_t status, - unsigned long data_size, - efi_char16_t *data) +static void __nocfi +virt_efi_reset_system(int reset_type, efi_status_t status, + unsigned long data_size, efi_char16_t *data) { if (down_trylock(&efi_runtime_lock)) { pr_warn("failed to invoke the reset_system() runtime service:\n" diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3dbddec07028..1c28a48915bb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1576,7 +1576,7 @@ config GPIO_TPS68470 are "output only" GPIOs. config GPIO_TQMX86 - tristate "TQ-Systems QTMX86 GPIO" + tristate "TQ-Systems TQMx86 GPIO" depends on MFD_TQMX86 || COMPILE_TEST depends on HAS_IOPORT_MAP select GPIOLIB_IRQCHIP diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 899335da93c7..7e29a2d8de1a 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -130,5 +130,6 @@ static struct i2c_driver gw_pld_driver = { }; module_i2c_driver(gw_pld_driver); +MODULE_DESCRIPTION("Gateworks I2C PLD GPIO expander"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index cd9b16dbe1a9..94f6fefc011b 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -168,5 +168,6 @@ static void __exit mc33880_exit(void) module_exit(mc33880_exit); MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); +MODULE_DESCRIPTION("MC33880 high-side/low-side switch GPIO driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 53b69abe6787..7c57eaeb0afe 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -438,5 +438,6 @@ static void __exit pcf857x_exit(void) } module_exit(pcf857x_exit); +MODULE_DESCRIPTION("Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 9fc1f3dd4190..a211a02d4b4a 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -438,4 +438,5 @@ static struct amba_driver pl061_gpio_driver = { }; module_amba_driver(pl061_gpio_driver); +MODULE_DESCRIPTION("Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 3a28c1f273c3..f2e7e8754d95 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -6,6 +6,7 @@ * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> */ +#include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/errno.h> #include <linux/gpio/driver.h> @@ -28,16 +29,25 @@ #define TQMX86_GPIIC 3 /* GPI Interrupt Configuration Register */ #define TQMX86_GPIIS 4 /* GPI Interrupt Status Register */ +#define TQMX86_GPII_NONE 0 #define TQMX86_GPII_FALLING BIT(0) #define TQMX86_GPII_RISING BIT(1) +/* Stored in irq_type as a trigger type, but not actually valid as a register + * value, so the name doesn't use "GPII" + */ +#define TQMX86_INT_BOTH (BIT(0) | BIT(1)) #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) #define TQMX86_GPII_BITS 2 +/* Stored in irq_type with GPII bits */ +#define TQMX86_INT_UNMASKED BIT(2) struct tqmx86_gpio_data { struct gpio_chip chip; void __iomem *io_base; int irq; + /* Lock must be held for accessing output and irq_type fields */ raw_spinlock_t spinlock; + DECLARE_BITMAP(output, TQMX86_NGPIO); u8 irq_type[TQMX86_NGPI]; }; @@ -64,15 +74,10 @@ static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); unsigned long flags; - u8 val; raw_spin_lock_irqsave(&gpio->spinlock, flags); - val = tqmx86_gpio_read(gpio, TQMX86_GPIOD); - if (value) - val |= BIT(offset); - else - val &= ~BIT(offset); - tqmx86_gpio_write(gpio, val, TQMX86_GPIOD); + __assign_bit(offset, gpio->output, value); + tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } @@ -107,21 +112,38 @@ static int tqmx86_gpio_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_OUT; } +static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset) + __must_hold(&gpio->spinlock) +{ + u8 type = TQMX86_GPII_NONE, gpiic; + + if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { + type = gpio->irq_type[offset] & TQMX86_GPII_MASK; + + if (type == TQMX86_INT_BOTH) + type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) + ? TQMX86_GPII_FALLING + : TQMX86_GPII_RISING; + } + + gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); + gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); + gpiic |= type << (offset * TQMX86_GPII_BITS); + tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); +} + static void tqmx86_gpio_irq_mask(struct irq_data *data) { unsigned int offset = (data->hwirq - TQMX86_NGPO); struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; - u8 gpiic, mask; - - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] &= ~TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(data)); } @@ -131,16 +153,12 @@ static void tqmx86_gpio_irq_unmask(struct irq_data *data) struct tqmx86_gpio_data *gpio = gpiochip_get_data( irq_data_get_irq_chip_data(data)); unsigned long flags; - u8 gpiic, mask; - - mask = TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS); gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(data)); + raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~mask; - gpiic |= gpio->irq_type[offset] << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] |= TQMX86_INT_UNMASKED; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); } @@ -151,7 +169,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) unsigned int offset = (data->hwirq - TQMX86_NGPO); unsigned int edge_type = type & IRQF_TRIGGER_MASK; unsigned long flags; - u8 new_type, gpiic; + u8 new_type; switch (edge_type) { case IRQ_TYPE_EDGE_RISING: @@ -161,19 +179,16 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type) new_type = TQMX86_GPII_FALLING; break; case IRQ_TYPE_EDGE_BOTH: - new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; + new_type = TQMX86_INT_BOTH; break; default: return -EINVAL; /* not supported */ } - gpio->irq_type[offset] = new_type; - raw_spin_lock_irqsave(&gpio->spinlock, flags); - gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); - gpiic &= ~((TQMX86_GPII_MASK) << (offset * TQMX86_GPII_BITS)); - gpiic |= new_type << (offset * TQMX86_GPII_BITS); - tqmx86_gpio_write(gpio, gpiic, TQMX86_GPIIC); + gpio->irq_type[offset] &= ~TQMX86_GPII_MASK; + gpio->irq_type[offset] |= new_type; + tqmx86_gpio_irq_config(gpio, offset); raw_spin_unlock_irqrestore(&gpio->spinlock, flags); return 0; @@ -184,8 +199,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned long irq_bits; - int i = 0; + unsigned long irq_bits, flags; + int i; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -194,6 +209,34 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); irq_bits = irq_status; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + /* + * Edge-both triggers are implemented by flipping the edge + * trigger after each interrupt, as the controller only supports + * either rising or falling edge triggers, but not both. + * + * Internally, the TQMx86 GPIO controller has separate status + * registers for rising and falling edge interrupts. GPIIC + * configures which bits from which register are visible in the + * interrupt status register GPIIS and defines what triggers the + * parent IRQ line. Writing to GPIIS always clears both rising + * and falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the edge-both + * trigger in software by first clearing the interrupt and then + * setting the new trigger based on the current GPIO input in + * tqmx86_gpio_irq_config() - even if an edge arrives between + * reading the input and setting the trigger, we will have a new + * interrupt pending. + */ + if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) + tqmx86_gpio_irq_config(gpio, i); + } + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, i + TQMX86_NGPO); @@ -277,6 +320,13 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); + /* + * Reading the previous output state is not possible with TQMx86 hardware. + * Initialize all outputs to 0 to have a defined state that matches the + * shadow register. + */ + tqmx86_gpio_write(gpio, 0, TQMX86_GPIOD); + chip = &gpio->chip; chip->label = "gpio-tqmx86"; chip->owner = THIS_MODULE; diff --git a/drivers/gpu/drm/amd/include/pptable.h b/drivers/gpu/drm/amd/include/pptable.h index 2e8e6c9875f6..f83ace2d7ec3 100644 --- a/drivers/gpu/drm/amd/include/pptable.h +++ b/drivers/gpu/drm/amd/include/pptable.h @@ -477,31 +477,30 @@ typedef struct _ATOM_PPLIB_STATE_V2 } ATOM_PPLIB_STATE_V2; typedef struct _StateArray{ - //how many states we have - UCHAR ucNumEntries; - - ATOM_PPLIB_STATE_V2 states[1]; + //how many states we have + UCHAR ucNumEntries; + + ATOM_PPLIB_STATE_V2 states[] /* __counted_by(ucNumEntries) */; }StateArray; typedef struct _ClockInfoArray{ - //how many clock levels we have - UCHAR ucNumEntries; - - //sizeof(ATOM_PPLIB_CLOCK_INFO) - UCHAR ucEntrySize; - - UCHAR clockInfo[]; + //how many clock levels we have + UCHAR ucNumEntries; + + //sizeof(ATOM_PPLIB_CLOCK_INFO) + UCHAR ucEntrySize; + + UCHAR clockInfo[]; }ClockInfoArray; typedef struct _NonClockInfoArray{ + //how many non-clock levels we have. normally should be same as number of states + UCHAR ucNumEntries; + //sizeof(ATOM_PPLIB_NONCLOCK_INFO) + UCHAR ucEntrySize; - //how many non-clock levels we have. normally should be same as number of states - UCHAR ucNumEntries; - //sizeof(ATOM_PPLIB_NONCLOCK_INFO) - UCHAR ucEntrySize; - - ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[]; + ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[] __counted_by(ucNumEntries); }NonClockInfoArray; typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record @@ -513,8 +512,10 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_Clock_Voltage_Dependency_Table; typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record @@ -529,8 +530,10 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_Clock_Voltage_Limit_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_Clock_Voltage_Limit_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_Clock_Voltage_Limit_Table; union _ATOM_PPLIB_CAC_Leakage_Record @@ -553,8 +556,10 @@ typedef union _ATOM_PPLIB_CAC_Leakage_Record ATOM_PPLIB_CAC_Leakage_Record; typedef struct _ATOM_PPLIB_CAC_Leakage_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_CAC_Leakage_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_CAC_Leakage_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_CAC_Leakage_Table; typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record @@ -568,8 +573,10 @@ typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Table { - UCHAR ucNumEntries; // Number of entries. - ATOM_PPLIB_PhaseSheddingLimits_Record entries[1]; // Dynamically allocate entries. + // Number of entries. + UCHAR ucNumEntries; + // Dynamically allocate entries. + ATOM_PPLIB_PhaseSheddingLimits_Record entries[] __counted_by(ucNumEntries); }ATOM_PPLIB_PhaseSheddingLimits_Table; typedef struct _VCEClockInfo{ @@ -580,8 +587,8 @@ typedef struct _VCEClockInfo{ }VCEClockInfo; typedef struct _VCEClockInfoArray{ - UCHAR ucNumEntries; - VCEClockInfo entries[1]; + UCHAR ucNumEntries; + VCEClockInfo entries[] __counted_by(ucNumEntries); }VCEClockInfoArray; typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record @@ -592,8 +599,8 @@ typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table { - UCHAR numEntries; - ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_VCE_State_Record @@ -604,8 +611,8 @@ typedef struct _ATOM_PPLIB_VCE_State_Record typedef struct _ATOM_PPLIB_VCE_State_Table { - UCHAR numEntries; - ATOM_PPLIB_VCE_State_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_VCE_State_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_VCE_State_Table; @@ -626,8 +633,8 @@ typedef struct _UVDClockInfo{ }UVDClockInfo; typedef struct _UVDClockInfoArray{ - UCHAR ucNumEntries; - UVDClockInfo entries[1]; + UCHAR ucNumEntries; + UVDClockInfo entries[] __counted_by(ucNumEntries); }UVDClockInfoArray; typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record @@ -638,8 +645,8 @@ typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table { - UCHAR numEntries; - ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_UVD_Table @@ -657,8 +664,8 @@ typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Record }ATOM_PPLIB_SAMClk_Voltage_Limit_Record; typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Table{ - UCHAR numEntries; - ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[]; + UCHAR numEntries; + ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_SAMClk_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_SAMU_Table @@ -675,8 +682,8 @@ typedef struct _ATOM_PPLIB_ACPClk_Voltage_Limit_Record }ATOM_PPLIB_ACPClk_Voltage_Limit_Record; typedef struct _ATOM_PPLIB_ACPClk_Voltage_Limit_Table{ - UCHAR numEntries; - ATOM_PPLIB_ACPClk_Voltage_Limit_Record entries[1]; + UCHAR numEntries; + ATOM_PPLIB_ACPClk_Voltage_Limit_Record entries[] __counted_by(numEntries); }ATOM_PPLIB_ACPClk_Voltage_Limit_Table; typedef struct _ATOM_PPLIB_ACP_Table @@ -743,9 +750,9 @@ typedef struct ATOM_PPLIB_VQ_Budgeting_Record{ } ATOM_PPLIB_VQ_Budgeting_Record; typedef struct ATOM_PPLIB_VQ_Budgeting_Table { - UCHAR revid; - UCHAR numEntries; - ATOM_PPLIB_VQ_Budgeting_Record entries[1]; + UCHAR revid; + UCHAR numEntries; + ATOM_PPLIB_VQ_Budgeting_Record entries[] __counted_by(numEntries); } ATOM_PPLIB_VQ_Budgeting_Table; #pragma pack() diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index bc241b593db1..b6257f34a7c6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -226,15 +226,17 @@ static int smu_v13_0_4_system_features_control(struct smu_context *smu, bool en) struct amdgpu_device *adev = smu->adev; int ret = 0; - if (!en && adev->in_s4) { - /* Adds a GFX reset as workaround just before sending the - * MP1_UNLOAD message to prevent GC/RLC/PMFW from entering - * an invalid state. - */ - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, - SMU_RESET_MODE_2, NULL); - if (ret) - return ret; + if (!en && !adev->in_s0ix) { + if (adev->in_s4) { + /* Adds a GFX reset as workaround just before sending the + * MP1_UNLOAD message to prevent GC/RLC/PMFW from entering + * an invalid state. + */ + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxDeviceDriverReset, + SMU_RESET_MODE_2, NULL); + if (ret) + return ret; + } ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL); } diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c b/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c index d8e449e6ebda..50cb8f7ee6b2 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_color_mgmt.c @@ -72,11 +72,6 @@ struct gamma_curve_sector { u32 segment_width; }; -struct gamma_curve_segment { - u32 start; - u32 end; -}; - static struct gamma_curve_sector sector_tbl[] = { { 0, 4, 4 }, { 16, 4, 4 }, diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index e8f385b9c618..28bfc48a9127 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -643,7 +643,9 @@ static int st7789v_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); - of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation); + ret = of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n"); drm_panel_add(&ctx->panel); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8f1730aeacc9..823d8d2da17c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -746,7 +746,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, dev->vram_size = pci_resource_len(pdev, 2); drm_info(&dev->drm, - "Register MMIO at 0x%pa size is %llu kiB\n", + "Register MMIO at 0x%pa size is %llu KiB\n", &rmmio_start, (uint64_t)rmmio_size / 1024); dev->rmmio = devm_ioremap(dev->drm.dev, rmmio_start, @@ -765,7 +765,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, fifo_size = pci_resource_len(pdev, 2); drm_info(&dev->drm, - "FIFO at %pa size is %llu kiB\n", + "FIFO at %pa size is %llu KiB\n", &fifo_start, (uint64_t)fifo_size / 1024); dev->fifo_mem = devm_memremap(dev->drm.dev, fifo_start, @@ -790,7 +790,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, * SVGA_REG_VRAM_SIZE. */ drm_info(&dev->drm, - "VRAM at %pa size is %llu kiB\n", + "VRAM at %pa size is %llu KiB\n", &dev->vram_start, (uint64_t)dev->vram_size / 1024); return 0; @@ -960,13 +960,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) vmw_read(dev_priv, SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); - /* - * Workaround for low memory 2D VMs to compensate for the - * allocation taken by fbdev - */ - if (!(dev_priv->capabilities & SVGA_CAP_3D)) - mem_size *= 3; - dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; dev_priv->max_primary_mem = vmw_read(dev_priv, SVGA_REG_MAX_PRIMARY_MEM); @@ -991,13 +984,13 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) dev_priv->max_primary_mem = dev_priv->vram_size; } drm_info(&dev_priv->drm, - "Legacy memory limits: VRAM = %llu kB, FIFO = %llu kB, surface = %u kB\n", + "Legacy memory limits: VRAM = %llu KiB, FIFO = %llu KiB, surface = %u KiB\n", (u64)dev_priv->vram_size / 1024, (u64)dev_priv->fifo_mem_size / 1024, dev_priv->memory_size / 1024); drm_info(&dev_priv->drm, - "MOB limits: max mob size = %u kB, max mob pages = %u\n", + "MOB limits: max mob size = %u KiB, max mob pages = %u\n", dev_priv->max_mob_size / 1024, dev_priv->max_mob_pages); ret = vmw_dma_masks(dev_priv); @@ -1015,7 +1008,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) (unsigned)dev_priv->max_gmr_pages); } drm_info(&dev_priv->drm, - "Maximum display memory size is %llu kiB\n", + "Maximum display memory size is %llu KiB\n", (uint64_t)dev_priv->max_primary_mem / 1024); /* Need mmio memory to check for fifo pitchlock cap. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 4ecaea0026fc..a1ce41e1c468 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -1043,9 +1043,6 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, unsigned bpp, unsigned depth); -bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height); int vmw_kms_present(struct vmw_private *dev_priv, struct drm_file *file_priv, struct vmw_framebuffer *vfb, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index a0b47c9b33f5..5bd967fbcf55 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -94,14 +94,14 @@ static int vmw_gmrid_man_get_node(struct ttm_resource_manager *man, } else new_max_pages = gman->max_gmr_pages * 2; if (new_max_pages > gman->max_gmr_pages && new_max_pages >= gman->used_gmr_pages) { - DRM_WARN("vmwgfx: increasing guest mob limits to %u kB.\n", + DRM_WARN("vmwgfx: increasing guest mob limits to %u KiB.\n", ((new_max_pages) << (PAGE_SHIFT - 10))); gman->max_gmr_pages = new_max_pages; } else { char buf[256]; snprintf(buf, sizeof(buf), - "vmwgfx, error: guest graphics is out of memory (mob limit at: %ukB).\n", + "vmwgfx, error: guest graphics is out of memory (mob limit at: %u KiB).\n", ((gman->max_gmr_pages) << (PAGE_SHIFT - 10))); vmw_host_printf(buf); DRM_WARN("%s", buf); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 13b2820cae51..00c4ff684130 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -224,7 +224,7 @@ static bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, new_image = vmw_du_cursor_plane_acquire_image(new_vps); changed = false; - if (old_image && new_image) + if (old_image && new_image && old_image != new_image) changed = memcmp(old_image, new_image, size) != 0; return changed; @@ -2171,13 +2171,12 @@ int vmw_kms_write_svga(struct vmw_private *vmw_priv, return 0; } +static bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, - uint32_t pitch, - uint32_t height) + u64 pitch, + u64 height) { - return ((u64) pitch * (u64) height) < (u64) - ((dev_priv->active_display_unit == vmw_du_screen_target) ? - dev_priv->max_primary_mem : dev_priv->vram_size); + return (pitch * height) < (u64)dev_priv->vram_size; } /** @@ -2873,25 +2872,18 @@ out_unref: enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + enum drm_mode_status ret; struct drm_device *dev = connector->dev; struct vmw_private *dev_priv = vmw_priv(dev); - u32 max_width = dev_priv->texture_max_width; - u32 max_height = dev_priv->texture_max_height; u32 assumed_cpp = 4; if (dev_priv->assume_16bpp) assumed_cpp = 2; - if (dev_priv->active_display_unit == vmw_du_screen_target) { - max_width = min(dev_priv->stdu_max_width, max_width); - max_height = min(dev_priv->stdu_max_height, max_height); - } - - if (max_width < mode->hdisplay) - return MODE_BAD_HVALUE; - - if (max_height < mode->vdisplay) - return MODE_BAD_VVALUE; + ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, + dev_priv->texture_max_height); + if (ret != MODE_OK) + return ret; if (!vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * assumed_cpp, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 2041c4d48daa..a04e0736318d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -43,7 +43,14 @@ #define vmw_connector_to_stdu(x) \ container_of(x, struct vmw_screen_target_display_unit, base.connector) - +/* + * Some renderers such as llvmpipe will align the width and height of their + * buffers to match their tile size. We need to keep this in mind when exposing + * modes to userspace so that this possible over-allocation will not exceed + * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the + * tile size of current renderers. + */ +#define GPU_TILE_SIZE 64 enum stdu_content_type { SAME_AS_DISPLAY = 0, @@ -85,11 +92,6 @@ struct vmw_stdu_update { SVGA3dCmdUpdateGBScreenTarget body; }; -struct vmw_stdu_dma { - SVGA3dCmdHeader header; - SVGA3dCmdSurfaceDMA body; -}; - struct vmw_stdu_surface_copy { SVGA3dCmdHeader header; SVGA3dCmdSurfaceCopy body; @@ -414,6 +416,7 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, { struct vmw_private *dev_priv; struct vmw_screen_target_display_unit *stdu; + struct drm_crtc_state *new_crtc_state; int ret; if (!crtc) { @@ -423,6 +426,7 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, stdu = vmw_crtc_to_stdu(crtc); dev_priv = vmw_priv(crtc->dev); + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (dev_priv->vkms_enabled) drm_crtc_vblank_off(crtc); @@ -434,6 +438,14 @@ static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, (void) vmw_stdu_update_st(dev_priv, stdu); + /* Don't destroy the Screen Target if we are only setting the + * display as inactive + */ + if (new_crtc_state->enable && + !new_crtc_state->active && + !new_crtc_state->mode_changed) + return; + ret = vmw_stdu_destroy_st(dev_priv, stdu); if (ret) DRM_ERROR("Failed to destroy Screen Target\n"); @@ -829,7 +841,41 @@ static void vmw_stdu_connector_destroy(struct drm_connector *connector) vmw_stdu_destroy(vmw_connector_to_stdu(connector)); } +static enum drm_mode_status +vmw_stdu_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + enum drm_mode_status ret; + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + u64 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4; + /* Align width and height to account for GPU tile over-alignment */ + u64 required_mem = ALIGN(mode->hdisplay, GPU_TILE_SIZE) * + ALIGN(mode->vdisplay, GPU_TILE_SIZE) * + assumed_cpp; + required_mem = ALIGN(required_mem, PAGE_SIZE); + + ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width, + dev_priv->stdu_max_height); + if (ret != MODE_OK) + return ret; + + ret = drm_mode_validate_size(mode, dev_priv->texture_max_width, + dev_priv->texture_max_height); + if (ret != MODE_OK) + return ret; + if (required_mem > dev_priv->max_primary_mem) + return MODE_MEM; + + if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE) + return MODE_MEM; + + if (required_mem > dev_priv->max_mob_size) + return MODE_MEM; + + return MODE_OK; +} static const struct drm_connector_funcs vmw_stdu_connector_funcs = { .dpms = vmw_du_connector_dpms, @@ -845,7 +891,7 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = { static const struct drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { .get_modes = vmw_connector_get_modes, - .mode_valid = vmw_connector_mode_valid + .mode_valid = vmw_stdu_connector_mode_valid }; diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c index 79116ad58620..476d613333a9 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c @@ -1749,6 +1749,7 @@ static void pf_release_vf_config(struct xe_gt *gt, unsigned int vfid) if (!xe_gt_is_media_type(gt)) { pf_release_vf_config_ggtt(gt, config); pf_release_vf_config_lmem(gt, config); + pf_update_vf_lmtt(gt_to_xe(gt), vfid); } pf_release_config_ctxs(gt, config); pf_release_config_dbs(gt, config); diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 02de2bf4f790..37e6d25593c2 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -1204,8 +1204,8 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, } /* match many more n-key devices */ - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - for (int i = 0; i < *rsize + 1; i++) { + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && *rsize > 15) { + for (int i = 0; i < *rsize - 15; i++) { /* offset to the count from 0x5a report part always 14 */ if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a && rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b1fa0378e8f4..74efda212c55 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1448,7 +1448,6 @@ static void implement(const struct hid_device *hid, u8 *report, hid_warn(hid, "%s() called with too large value %d (n: %d)! (%s)\n", __func__, value, n, current->comm); - WARN_ON(1); value &= m; } } diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 87a961cae775..d5abfe652fb5 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3366,6 +3366,8 @@ static const char *keys[KEY_MAX + 1] = { [KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable", [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", + [KEY_ACCESSIBILITY] = "Accessibility", + [KEY_DO_NOT_DISTURB] = "DoNotDisturb", [KEY_DICTATE] = "Dictate", [KEY_MICMUTE] = "MicrophoneMute", [KEY_BRIGHTNESS_MIN] = "BrightnessMin", diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 61d2a21affa2..72d56ee7ce1b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -423,6 +423,8 @@ #define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF #define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8 #define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82 +#define I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN 0x2F2C +#define I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN 0x4116 #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e03d300d2bac..c9094a4f281e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -377,6 +377,10 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), @@ -833,9 +837,18 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; } + if ((usage->hid & 0xf0) == 0x90) { /* SystemControl*/ + switch (usage->hid & 0xf) { + case 0xb: map_key_clear(KEY_DO_NOT_DISTURB); break; + default: goto ignore; + } + break; + } + if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ switch (usage->hid & 0xf) { case 0x9: map_key_clear(KEY_MICMUTE); break; + case 0xa: map_key_clear(KEY_ACCESSIBILITY); break; default: goto ignore; } break; diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 3c3c497b6b91..37958edec55f 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1284,8 +1284,10 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, */ msleep(50); - if (retval) + if (retval) { + kfree(dj_report); return retval; + } } /* diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index b81d5bcc76a7..400d70e6dbe2 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -27,6 +27,7 @@ #include "usbhid/usbhid.h" #include "hid-ids.h" +MODULE_DESCRIPTION("Support for Logitech devices relying on the HID++ specification"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>"); diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index b4a97803eca3..3062daf68d31 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -2725,13 +2725,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, ret = joycon_power_supply_create(ctlr); if (ret) { hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); - goto err_close; + goto err_ida; } ret = joycon_input_create(ctlr); if (ret) { hid_err(hdev, "Failed to create input device; ret=%d\n", ret); - goto err_close; + goto err_ida; } ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; @@ -2739,6 +2739,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_dbg(hdev, "probe - success\n"); return 0; +err_ida: + ida_free(&nintendo_player_id_allocator, ctlr->player_id); err_close: hid_hw_close(hdev); err_stop: diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c index 58b15750dbb0..ff9078ad1961 100644 --- a/drivers/hid/hid-nvidia-shield.c +++ b/drivers/hid/hid-nvidia-shield.c @@ -283,7 +283,9 @@ static struct input_dev *shield_haptics_create( return haptics; input_set_capability(haptics, EV_FF, FF_RUMBLE); - input_ff_create_memless(haptics, NULL, play_effect); + ret = input_ff_create_memless(haptics, NULL, play_effect); + if (ret) + goto err; ret = input_register_device(haptics); if (ret) diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index 5b91fb106cfc..091e37933225 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -31,6 +31,7 @@ struct i2c_hid_of_elan { struct regulator *vcc33; struct regulator *vccio; struct gpio_desc *reset_gpio; + bool no_reset_on_power_off; const struct elan_i2c_hid_chip_data *chip_data; }; @@ -40,17 +41,17 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) container_of(ops, struct i2c_hid_of_elan, ops); int ret; + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->vcc33) { ret = regulator_enable(ihid_elan->vcc33); if (ret) - return ret; + goto err_deassert_reset; } ret = regulator_enable(ihid_elan->vccio); - if (ret) { - regulator_disable(ihid_elan->vcc33); - return ret; - } + if (ret) + goto err_disable_vcc33; if (ihid_elan->chip_data->post_power_delay_ms) msleep(ihid_elan->chip_data->post_power_delay_ms); @@ -60,6 +61,15 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms); return 0; + +err_disable_vcc33: + if (ihid_elan->vcc33) + regulator_disable(ihid_elan->vcc33); +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static void elan_i2c_hid_power_down(struct i2chid_ops *ops) @@ -67,7 +77,14 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) struct i2c_hid_of_elan *ihid_elan = container_of(ops, struct i2c_hid_of_elan, ops); - gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + /* + * Do not assert reset when the hardware allows for it to remain + * deasserted regardless of the state of the (shared) power supply to + * avoid wasting power when the supply is left on. + */ + if (!ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms) msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms); @@ -79,6 +96,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) static int i2c_hid_of_elan_probe(struct i2c_client *client) { struct i2c_hid_of_elan *ihid_elan; + int ret; ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); if (!ihid_elan) @@ -93,21 +111,38 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client) if (IS_ERR(ihid_elan->reset_gpio)) return PTR_ERR(ihid_elan->reset_gpio); + ihid_elan->no_reset_on_power_off = of_property_read_bool(client->dev.of_node, + "no-reset-on-power-off"); + ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); - if (IS_ERR(ihid_elan->vccio)) - return PTR_ERR(ihid_elan->vccio); + if (IS_ERR(ihid_elan->vccio)) { + ret = PTR_ERR(ihid_elan->vccio); + goto err_deassert_reset; + } ihid_elan->chip_data = device_get_match_data(&client->dev); if (ihid_elan->chip_data->main_supply_name) { ihid_elan->vcc33 = devm_regulator_get(&client->dev, ihid_elan->chip_data->main_supply_name); - if (IS_ERR(ihid_elan->vcc33)) - return PTR_ERR(ihid_elan->vcc33); + if (IS_ERR(ihid_elan->vcc33)) { + ret = PTR_ERR(ihid_elan->vcc33); + goto err_deassert_reset; + } } - return i2c_hid_core_probe(client, &ihid_elan->ops, - ihid_elan->chip_data->hid_descriptor_address, 0); + ret = i2c_hid_core_probe(client, &ihid_elan->ops, + ihid_elan->chip_data->hid_descriptor_address, 0); + if (ret) + goto err_deassert_reset; + + return 0; + +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-hid/ishtp/loader.c index 993f8b390e57..fcca070bdecb 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.c +++ b/drivers/hid/intel-ish-hid/ishtp/loader.c @@ -84,8 +84,8 @@ static int loader_write_message(struct ishtp_device *dev, void *buf, int len) static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, void *resp, int resp_len) { - struct loader_msg_header *req_hdr = req; - struct loader_msg_header *resp_hdr = resp; + union loader_msg_header req_hdr; + union loader_msg_header resp_hdr; struct device *devc = dev->devc; int rv; @@ -93,34 +93,37 @@ static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, dev->fw_loader_rx_size = resp_len; rv = loader_write_message(dev, req, req_len); + req_hdr.val32 = le32_to_cpup(req); + if (rv < 0) { - dev_err(devc, "write cmd %u failed:%d\n", req_hdr->command, rv); + dev_err(devc, "write cmd %u failed:%d\n", req_hdr.command, rv); return rv; } /* Wait the ACK */ wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received, ISHTP_LOADER_TIMEOUT); + resp_hdr.val32 = le32_to_cpup(resp); dev->fw_loader_rx_size = 0; dev->fw_loader_rx_buf = NULL; if (!dev->fw_loader_received) { - dev_err(devc, "wait response of cmd %u timeout\n", req_hdr->command); + dev_err(devc, "wait response of cmd %u timeout\n", req_hdr.command); return -ETIMEDOUT; } - if (!resp_hdr->is_response) { - dev_err(devc, "not a response for %u\n", req_hdr->command); + if (!resp_hdr.is_response) { + dev_err(devc, "not a response for %u\n", req_hdr.command); return -EBADMSG; } - if (req_hdr->command != resp_hdr->command) { - dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr->command, - resp_hdr->command); + if (req_hdr.command != resp_hdr.command) { + dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr.command, + resp_hdr.command); return -EBADMSG; } - if (resp_hdr->status) { - dev_err(devc, "cmd %u failed %u\n", req_hdr->command, resp_hdr->status); + if (resp_hdr.status) { + dev_err(devc, "cmd %u failed %u\n", req_hdr.command, resp_hdr.status); return -EIO; } @@ -138,12 +141,13 @@ static void release_dma_bufs(struct ishtp_device *dev, struct loader_xfer_dma_fragment *fragment, void **dma_bufs, u32 fragment_size) { + dma_addr_t dma_addr; int i; for (i = 0; i < FRAGMENT_MAX_NUM; i++) { if (dma_bufs[i]) { - dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], - fragment->fragment_tbl[i].ddr_adrs); + dma_addr = le64_to_cpu(fragment->fragment_tbl[i].ddr_adrs); + dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], dma_addr); dma_bufs[i] = NULL; } } @@ -156,29 +160,33 @@ static void release_dma_bufs(struct ishtp_device *dev, * @fragment: The ISHTP firmware fragment descriptor * @dma_bufs: The array of DMA fragment buffers * @fragment_size: The size of a single DMA fragment + * @fragment_count: Number of fragments * * Return: 0 on success, negative error code on failure */ static int prepare_dma_bufs(struct ishtp_device *dev, const struct firmware *ish_fw, struct loader_xfer_dma_fragment *fragment, - void **dma_bufs, u32 fragment_size) + void **dma_bufs, u32 fragment_size, u32 fragment_count) { + dma_addr_t dma_addr; u32 offset = 0; + u32 length; int i; - for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) { - dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, - &fragment->fragment_tbl[i].ddr_adrs, GFP_KERNEL); + for (i = 0; i < fragment_count && offset < ish_fw->size; i++) { + dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, &dma_addr, GFP_KERNEL); if (!dma_bufs[i]) return -ENOMEM; - fragment->fragment_tbl[i].length = clamp(ish_fw->size - offset, 0, fragment_size); - fragment->fragment_tbl[i].fw_off = offset; - memcpy(dma_bufs[i], ish_fw->data + offset, fragment->fragment_tbl[i].length); + fragment->fragment_tbl[i].ddr_adrs = cpu_to_le64(dma_addr); + length = clamp(ish_fw->size - offset, 0, fragment_size); + fragment->fragment_tbl[i].length = cpu_to_le32(length); + fragment->fragment_tbl[i].fw_off = cpu_to_le32(offset); + memcpy(dma_bufs[i], ish_fw->data + offset, length); clflush_cache_range(dma_bufs[i], fragment_size); - offset += fragment->fragment_tbl[i].length; + offset += length; } return 0; @@ -206,17 +214,17 @@ void ishtp_loader_work(struct work_struct *work) { DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM); struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader); - struct loader_xfer_query query = { - .header.command = LOADER_CMD_XFER_QUERY, - }; - struct loader_start start = { - .header.command = LOADER_CMD_START, - }; + union loader_msg_header query_hdr = { .command = LOADER_CMD_XFER_QUERY, }; + union loader_msg_header start_hdr = { .command = LOADER_CMD_START, }; + union loader_msg_header fragment_hdr = { .command = LOADER_CMD_XFER_FRAGMENT, }; + struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), }; + struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), }; union loader_recv_message recv_msg; char *filename = dev->driver_data->fw_filename; const struct firmware *ish_fw; void *dma_bufs[FRAGMENT_MAX_NUM] = {}; u32 fragment_size; + u32 fragment_count; int retry = ISHTP_LOADER_RETRY_TIMES; int rv; @@ -226,23 +234,24 @@ void ishtp_loader_work(struct work_struct *work) return; } - fragment->fragment.header.command = LOADER_CMD_XFER_FRAGMENT; - fragment->fragment.xfer_mode = LOADER_XFER_MODE_DMA; - fragment->fragment.is_last = 1; - fragment->fragment.size = ish_fw->size; + fragment->fragment.header = cpu_to_le32(fragment_hdr.val32); + fragment->fragment.xfer_mode = cpu_to_le32(LOADER_XFER_MODE_DMA); + fragment->fragment.is_last = cpu_to_le32(1); + fragment->fragment.size = cpu_to_le32(ish_fw->size); /* Calculate the size of a single DMA fragment */ fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM)); /* Calculate the count of DMA fragments */ - fragment->fragment_cnt = DIV_ROUND_UP(ish_fw->size, fragment_size); + fragment_count = DIV_ROUND_UP(ish_fw->size, fragment_size); + fragment->fragment_cnt = cpu_to_le32(fragment_count); - rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size); + rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size, fragment_count); if (rv) { dev_err(dev->devc, "prepare DMA buffer failed.\n"); goto out; } do { - query.image_size = ish_fw->size; + query.image_size = cpu_to_le32(ish_fw->size); rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data, sizeof(struct loader_xfer_query_ack)); if (rv) @@ -255,7 +264,7 @@ void ishtp_loader_work(struct work_struct *work) recv_msg.query_ack.version_build); rv = loader_xfer_cmd(dev, fragment, - struct_size(fragment, fragment_tbl, fragment->fragment_cnt), + struct_size(fragment, fragment_tbl, fragment_count), recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack)); if (rv) continue; /* try again if failed */ diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.h b/drivers/hid/intel-ish-hid/ishtp/loader.h index 7aa45ebc3f7b..308b96085a4d 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.h +++ b/drivers/hid/intel-ish-hid/ishtp/loader.h @@ -30,19 +30,23 @@ struct work_struct; #define LOADER_XFER_MODE_DMA BIT(0) /** - * struct loader_msg_header - ISHTP firmware loader message header + * union loader_msg_header - ISHTP firmware loader message header * @command: Command type * @is_response: Indicates if the message is a response * @has_next: Indicates if there is a next message * @reserved: Reserved for future use * @status: Status of the message - */ -struct loader_msg_header { - __le32 command:7; - __le32 is_response:1; - __le32 has_next:1; - __le32 reserved:15; - __le32 status:8; + * @val32: entire header as a 32-bit value + */ +union loader_msg_header { + struct { + __u32 command:7; + __u32 is_response:1; + __u32 has_next:1; + __u32 reserved:15; + __u32 status:8; + }; + __u32 val32; }; /** @@ -51,7 +55,7 @@ struct loader_msg_header { * @image_size: Size of the image */ struct loader_xfer_query { - struct loader_msg_header header; + __le32 header; __le32 image_size; }; @@ -103,7 +107,7 @@ struct loader_capability { * @capability: Loader capability */ struct loader_xfer_query_ack { - struct loader_msg_header header; + __le32 header; __le16 version_major; __le16 version_minor; __le16 version_hotfix; @@ -122,7 +126,7 @@ struct loader_xfer_query_ack { * @is_last: Is last */ struct loader_xfer_fragment { - struct loader_msg_header header; + __le32 header; __le32 xfer_mode; __le32 offset; __le32 size; @@ -134,7 +138,7 @@ struct loader_xfer_fragment { * @header: Header of the message */ struct loader_xfer_fragment_ack { - struct loader_msg_header header; + __le32 header; }; /** @@ -170,7 +174,7 @@ struct loader_xfer_dma_fragment { * @header: Header of the message */ struct loader_start { - struct loader_msg_header header; + __le32 header; }; /** @@ -178,10 +182,11 @@ struct loader_start { * @header: Header of the message */ struct loader_start_ack { - struct loader_msg_header header; + __le32 header; }; union loader_recv_message { + __le32 header; struct loader_xfer_query_ack query_ack; struct loader_xfer_fragment_ack fragment_ack; struct loader_start_ack start_ack; diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 31ecb2c7e978..4eccbcd0fbfc 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -138,7 +138,6 @@ struct synquacer_i2c { int irq; struct device *dev; void __iomem *base; - struct clk *pclk; u32 pclkrate; u32 speed_khz; u32 timeout_ms; @@ -535,6 +534,7 @@ static const struct i2c_adapter synquacer_i2c_ops = { static int synquacer_i2c_probe(struct platform_device *pdev) { struct synquacer_i2c *i2c; + struct clk *pclk; u32 bus_speed; int ret; @@ -550,13 +550,12 @@ static int synquacer_i2c_probe(struct platform_device *pdev) device_property_read_u32(&pdev->dev, "socionext,pclk-rate", &i2c->pclkrate); - i2c->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); - if (IS_ERR(i2c->pclk)) - return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk), + pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pclk), "failed to get and enable clock\n"); - dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk); - i2c->pclkrate = clk_get_rate(i2c->pclk); + i2c->pclkrate = clk_get_rate(pclk); if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE || i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index bbd366dcb69a..6a42b27c4599 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -71,7 +71,6 @@ struct silead_ts_data { struct regulator_bulk_data regulators[2]; char fw_name[64]; struct touchscreen_properties prop; - u32 max_fingers; u32 chip_id; struct input_mt_pos pos[SILEAD_MAX_FINGERS]; int slots[SILEAD_MAX_FINGERS]; @@ -136,7 +135,7 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) touchscreen_parse_properties(data->input, true, &data->prop); silead_apply_efi_fw_min_max(data); - input_mt_init_slots(data->input, data->max_fingers, + input_mt_init_slots(data->input, SILEAD_MAX_FINGERS, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); @@ -256,10 +255,10 @@ static void silead_ts_read_data(struct i2c_client *client) return; } - if (buf[0] > data->max_fingers) { + if (buf[0] > SILEAD_MAX_FINGERS) { dev_warn(dev, "More touches reported then supported %d > %d\n", - buf[0], data->max_fingers); - buf[0] = data->max_fingers; + buf[0], SILEAD_MAX_FINGERS); + buf[0] = SILEAD_MAX_FINGERS; } if (silead_ts_handle_pen_data(data, buf)) @@ -315,7 +314,6 @@ sync: static int silead_ts_init(struct i2c_client *client) { - struct silead_ts_data *data = i2c_get_clientdata(client); int error; error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, @@ -325,7 +323,7 @@ static int silead_ts_init(struct i2c_client *client) usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); error = i2c_smbus_write_byte_data(client, SILEAD_REG_TOUCH_NR, - data->max_fingers); + SILEAD_MAX_FINGERS); if (error) goto i2c_write_err; usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); @@ -591,13 +589,6 @@ static void silead_ts_read_props(struct i2c_client *client) const char *str; int error; - error = device_property_read_u32(dev, "silead,max-fingers", - &data->max_fingers); - if (error) { - dev_dbg(dev, "Max fingers read error %d\n", error); - data->max_fingers = 5; /* Most devices handle up-to 5 fingers */ - } - error = device_property_read_string(dev, "firmware-name", &str); if (!error) snprintf(data->fw_name, sizeof(data->fw_name), diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 2fde1302a584..2d5945c982bd 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -129,7 +129,8 @@ static inline int check_feature_gpt_level(void) static inline bool amd_iommu_gt_ppr_supported(void) { return (check_feature(FEATURE_GT) && - check_feature(FEATURE_PPR)); + check_feature(FEATURE_PPR) && + check_feature(FEATURE_EPHSUP)); } static inline u64 iommu_virt_to_phys(void *vaddr) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index a18e74878f68..27e293727095 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -1626,8 +1626,17 @@ static void __init free_pci_segments(void) } } +static void __init free_sysfs(struct amd_iommu *iommu) +{ + if (iommu->iommu.dev) { + iommu_device_unregister(&iommu->iommu); + iommu_device_sysfs_remove(&iommu->iommu); + } +} + static void __init free_iommu_one(struct amd_iommu *iommu) { + free_sysfs(iommu); free_cwwb_sem(iommu); free_command_buffer(iommu); free_event_buffer(iommu); diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 52d83730a22a..c2703599bb16 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -2032,7 +2032,6 @@ static int do_attach(struct iommu_dev_data *dev_data, struct protection_domain *domain) { struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data); - struct pci_dev *pdev; int ret = 0; /* Update data structures */ @@ -2047,30 +2046,13 @@ static int do_attach(struct iommu_dev_data *dev_data, domain->dev_iommu[iommu->index] += 1; domain->dev_cnt += 1; - pdev = dev_is_pci(dev_data->dev) ? to_pci_dev(dev_data->dev) : NULL; + /* Setup GCR3 table */ if (pdom_is_sva_capable(domain)) { ret = init_gcr3_table(dev_data, domain); if (ret) return ret; - - if (pdev) { - pdev_enable_caps(pdev); - - /* - * Device can continue to function even if IOPF - * enablement failed. Hence in error path just - * disable device PRI support. - */ - if (amd_iommu_iopf_add_device(iommu, dev_data)) - pdev_disable_cap_pri(pdev); - } - } else if (pdev) { - pdev_enable_cap_ats(pdev); } - /* Update device table */ - amd_iommu_dev_update_dte(dev_data, true); - return ret; } @@ -2163,6 +2145,11 @@ static void detach_device(struct device *dev) do_detach(dev_data); +out: + spin_unlock(&dev_data->lock); + + spin_unlock_irqrestore(&domain->lock, flags); + /* Remove IOPF handler */ if (ppr) amd_iommu_iopf_remove_device(iommu, dev_data); @@ -2170,10 +2157,6 @@ static void detach_device(struct device *dev) if (dev_is_pci(dev)) pdev_disable_caps(to_pci_dev(dev)); -out: - spin_unlock(&dev_data->lock); - - spin_unlock_irqrestore(&domain->lock, flags); } static struct iommu_device *amd_iommu_probe_device(struct device *dev) @@ -2485,6 +2468,7 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev); struct protection_domain *domain = to_pdomain(dom); struct amd_iommu *iommu = get_amd_iommu_from_dev(dev); + struct pci_dev *pdev; int ret; /* @@ -2517,7 +2501,23 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, } #endif - iommu_completion_wait(iommu); + pdev = dev_is_pci(dev_data->dev) ? to_pci_dev(dev_data->dev) : NULL; + if (pdev && pdom_is_sva_capable(domain)) { + pdev_enable_caps(pdev); + + /* + * Device can continue to function even if IOPF + * enablement failed. Hence in error path just + * disable device PRI support. + */ + if (amd_iommu_iopf_add_device(iommu, dev_data)) + pdev_disable_cap_pri(pdev); + } else if (pdev) { + pdev_enable_cap_ats(pdev); + } + + /* Update device table */ + amd_iommu_dev_update_dte(dev_data, true); return ret; } diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 091423bb8aac..7c67d69f0b8c 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -222,8 +222,7 @@ int amd_iommu_iopf_init(struct amd_iommu *iommu) if (iommu->iopf_queue) return ret; - snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), - "amdiommu-%#x-iopfq", + snprintf(iommu->iopfq_name, sizeof(iommu->iopfq_name), "amdvi-%#x", PCI_SEG_DEVID_TO_SBDF(iommu->pci_seg->id, iommu->devid)); iommu->iopf_queue = iopf_queue_alloc(iommu->iopfq_name); @@ -249,40 +248,26 @@ void amd_iommu_page_response(struct device *dev, struct iopf_fault *evt, int amd_iommu_iopf_add_device(struct amd_iommu *iommu, struct iommu_dev_data *dev_data) { - unsigned long flags; int ret = 0; if (!dev_data->pri_enabled) return ret; - raw_spin_lock_irqsave(&iommu->lock, flags); - - if (!iommu->iopf_queue) { - ret = -EINVAL; - goto out_unlock; - } + if (!iommu->iopf_queue) + return -EINVAL; ret = iopf_queue_add_device(iommu->iopf_queue, dev_data->dev); if (ret) - goto out_unlock; + return ret; dev_data->ppr = true; - -out_unlock: - raw_spin_unlock_irqrestore(&iommu->lock, flags); - return ret; + return 0; } /* Its assumed that caller has verified that device was added to iopf queue */ void amd_iommu_iopf_remove_device(struct amd_iommu *iommu, struct iommu_dev_data *dev_data) { - unsigned long flags; - - raw_spin_lock_irqsave(&iommu->lock, flags); - iopf_queue_remove_device(iommu->iopf_queue, dev_data->dev); dev_data->ppr = false; - - raw_spin_unlock_irqrestore(&iommu->lock, flags); } diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index f731e4b2a417..43520e7275cc 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -686,15 +686,15 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, struct device *dev /* Check the domain allows at least some access to the device... */ if (map) { - dma_addr_t base = dma_range_map_min(map); - if (base > domain->geometry.aperture_end || + if (dma_range_map_min(map) > domain->geometry.aperture_end || dma_range_map_max(map) < domain->geometry.aperture_start) { pr_warn("specified DMA range outside IOMMU capability\n"); return -EFAULT; } - /* ...then finally give it a kicking to make sure it fits */ - base_pfn = max(base, domain->geometry.aperture_start) >> order; } + /* ...then finally give it a kicking to make sure it fits */ + base_pfn = max_t(unsigned long, base_pfn, + domain->geometry.aperture_start >> order); /* start_pfn is always nonzero for an already-initialised domain */ mutex_lock(&cookie->mutex); diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 40ebf1726393..3c755d5dad6e 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1846,28 +1846,22 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); - int ret = 0; if (!info->map) return -EINVAL; - raw_spin_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm) { struct its_vlpi_map *maps; maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps), GFP_ATOMIC); - if (!maps) { - ret = -ENOMEM; - goto out; - } + if (!maps) + return -ENOMEM; its_dev->event_map.vm = info->map->vm; its_dev->event_map.vlpi_maps = maps; } else if (its_dev->event_map.vm != info->map->vm) { - ret = -EINVAL; - goto out; + return -EINVAL; } /* Get our private copy of the mapping information */ @@ -1899,46 +1893,32 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) its_dev->event_map.nr_vlpis++; } -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); struct its_vlpi_map *map; - int ret = 0; - - raw_spin_lock(&its_dev->event_map.vlpi_lock); map = get_vlpi_map(d); - if (!its_dev->event_map.vm || !map) { - ret = -EINVAL; - goto out; - } + if (!its_dev->event_map.vm || !map) + return -EINVAL; /* Copy our mapping information to the incoming request */ *info->map = *map; -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_unmap(struct irq_data *d) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); - int ret = 0; - - raw_spin_lock(&its_dev->event_map.vlpi_lock); - if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) { - ret = -EINVAL; - goto out; - } + if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) + return -EINVAL; /* Drop the virtual mapping */ its_send_discard(its_dev, event); @@ -1962,9 +1942,7 @@ static int its_vlpi_unmap(struct irq_data *d) kfree(its_dev->event_map.vlpi_maps); } -out: - raw_spin_unlock(&its_dev->event_map.vlpi_lock); - return ret; + return 0; } static int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info) @@ -1992,6 +1970,8 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) if (!is_v4(its_dev->its)) return -EINVAL; + guard(raw_spinlock_irq)(&its_dev->event_map.vlpi_lock); + /* Unmap request? */ if (!info) return its_vlpi_unmap(d); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 9e71c4428814..4f3a12383a1e 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -253,8 +253,9 @@ IRQCHIP_DECLARE(andes, "andestech,cpu-intc", riscv_intc_init); static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, const unsigned long end) { - struct fwnode_handle *fn; struct acpi_madt_rintc *rintc; + struct fwnode_handle *fn; + int rc; rintc = (struct acpi_madt_rintc *)header; @@ -273,7 +274,11 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header, return -ENOMEM; } - return riscv_intc_init_common(fn, &riscv_intc_chip); + rc = riscv_intc_init_common(fn, &riscv_intc_chip); + if (rc) + irq_domain_free_fwnode(fn); + + return rc; } IRQCHIP_ACPI_DECLARE(riscv_intc, ACPI_MADT_TYPE_RINTC, NULL, diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 8fb183ced1e7..9e22f7e378f5 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -85,7 +85,7 @@ struct plic_handler { struct plic_priv *priv; }; static int plic_parent_irq __ro_after_init; -static bool plic_cpuhp_setup_done __ro_after_init; +static bool plic_global_setup_done __ro_after_init; static DEFINE_PER_CPU(struct plic_handler, plic_handlers); static int plic_irq_set_type(struct irq_data *d, unsigned int type); @@ -487,10 +487,8 @@ static int plic_probe(struct platform_device *pdev) unsigned long plic_quirks = 0; struct plic_handler *handler; u32 nr_irqs, parent_hwirq; - struct irq_domain *domain; struct plic_priv *priv; irq_hw_number_t hwirq; - bool cpuhp_setup; if (is_of_node(dev->fwnode)) { const struct of_device_id *id; @@ -549,14 +547,6 @@ static int plic_probe(struct platform_device *pdev) continue; } - /* Find parent domain and register chained handler */ - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); - if (!plic_parent_irq && domain) { - plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); - if (plic_parent_irq) - irq_set_chained_handler(plic_parent_irq, plic_handle_irq); - } - /* * When running in M-mode we need to ignore the S-mode handler. * Here we assume it always comes later, but that might be a @@ -597,25 +587,35 @@ done: goto fail_cleanup_contexts; /* - * We can have multiple PLIC instances so setup cpuhp state + * We can have multiple PLIC instances so setup global state * and register syscore operations only once after context * handlers of all online CPUs are initialized. */ - if (!plic_cpuhp_setup_done) { - cpuhp_setup = true; + if (!plic_global_setup_done) { + struct irq_domain *domain; + bool global_setup = true; + for_each_online_cpu(cpu) { handler = per_cpu_ptr(&plic_handlers, cpu); if (!handler->present) { - cpuhp_setup = false; + global_setup = false; break; } } - if (cpuhp_setup) { + + if (global_setup) { + /* Find parent domain and register chained handler */ + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY); + if (domain) + plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); + if (plic_parent_irq) + irq_set_chained_handler(plic_parent_irq, plic_handle_irq); + cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING, "irqchip/sifive/plic:starting", plic_starting_cpu, plic_dying_cpu); register_syscore_ops(&plic_irq_syscore_ops); - plic_cpuhp_setup_done = true; + plic_global_setup_done = true; } } diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 40a8ebfcfce2..4bd4e324abc9 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -301,10 +301,10 @@ static int ipu6_isys_stream_start(struct ipu6_isys_video *av, out_requeue: if (bl && bl->nbufs) ipu6_isys_buffer_list_queue(bl, - (IPU6_ISYS_BUFFER_LIST_FL_INCOMING | - error) ? + IPU6_ISYS_BUFFER_LIST_FL_INCOMING | + (error ? IPU6_ISYS_BUFFER_LIST_FL_SET_STATE : - 0, error ? VB2_BUF_STATE_ERROR : + 0), error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_QUEUED); flush_firmware_streamon_fail(stream); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 5992138c7290..8b9b77719bb1 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -678,6 +678,12 @@ static int isys_notifier_bound(struct v4l2_async_notifier *notifier, container_of(asc, struct sensor_async_sd, asc); int ret; + if (s_asd->csi2.port >= isys->pdata->ipdata->csi2.nports) { + dev_err(&isys->adev->auxdev.dev, "invalid csi2 port %u\n", + s_asd->csi2.port); + return -EINVAL; + } + ret = ipu_bridge_instantiate_vcm(sd->dev); if (ret) { dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n"); @@ -925,39 +931,18 @@ static const struct dev_pm_ops isys_pm_ops = { .resume = isys_resume, }; -static void isys_remove(struct auxiliary_device *auxdev) +static void free_fw_msg_bufs(struct ipu6_isys *isys) { - struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); - struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); - struct ipu6_device *isp = adev->isp; + struct device *dev = &isys->adev->auxdev.dev; struct isys_fw_msgs *fwmsg, *safe; - unsigned int i; list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); + dma_free_attrs(dev, sizeof(struct isys_fw_msgs), fwmsg, + fwmsg->dma_addr, 0); list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) - dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - - isys_unregister_devices(isys); - isys_notifier_cleanup(isys); - - cpu_latency_qos_remove_request(&isys->pm_qos); - - if (!isp->secure_mode) { - ipu6_cpd_free_pkg_dir(adev); - ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); - release_firmware(adev->fw); - } - - for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); - - isys_iwake_watermark_cleanup(isys); - mutex_destroy(&isys->stream_mutex); - mutex_destroy(&isys->mutex); + dma_free_attrs(dev, sizeof(struct isys_fw_msgs), fwmsg, + fwmsg->dma_addr, 0); } static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount) @@ -1140,12 +1125,14 @@ static int isys_probe(struct auxiliary_device *auxdev, ret = isys_register_devices(isys); if (ret) - goto out_remove_pkg_dir_shared_buffer; + goto free_fw_msg_bufs; ipu6_mmu_hw_cleanup(adev->mmu); return 0; +free_fw_msg_bufs: + free_fw_msg_bufs(isys); out_remove_pkg_dir_shared_buffer: if (!isp->secure_mode) ipu6_cpd_free_pkg_dir(adev); @@ -1167,6 +1154,34 @@ release_firmware: return ret; } +static void isys_remove(struct auxiliary_device *auxdev) +{ + struct ipu6_bus_device *adev = auxdev_to_adev(auxdev); + struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev); + struct ipu6_device *isp = adev->isp; + unsigned int i; + + free_fw_msg_bufs(isys); + + isys_unregister_devices(isys); + isys_notifier_cleanup(isys); + + cpu_latency_qos_remove_request(&isys->pm_qos); + + if (!isp->secure_mode) { + ipu6_cpd_free_pkg_dir(adev); + ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt); + release_firmware(adev->fw); + } + + for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + + isys_iwake_watermark_cleanup(isys); + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); +} + struct fwmsg { int type; char *msg; diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index d2bebd208461..bbd646378ab3 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -285,7 +285,7 @@ EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6); #define IPU6_ISYS_CSI2_NPORTS 4 #define IPU6SE_ISYS_CSI2_NPORTS 4 #define IPU6_TGL_ISYS_CSI2_NPORTS 8 -#define IPU6EP_MTL_ISYS_CSI2_NPORTS 4 +#define IPU6EP_MTL_ISYS_CSI2_NPORTS 6 static void ipu6_internal_pdata_init(struct ipu6_device *isp) { @@ -727,9 +727,6 @@ static void ipu6_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - release_firmware(isp->cpd_fw); ipu6_mmu_cleanup(psys_mmu); diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 89b582a221ab..f04a89584334 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -677,10 +677,13 @@ static int mei_csi_probe(struct mei_cl_device *cldev, return -ENODEV; ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb); + put_device(&ipu->dev); if (ret < 0) return ret; - if (WARN_ON(!dev_fwnode(dev))) + if (!dev_fwnode(dev)) { + dev_err(dev, "mei-csi probed without device fwnode!\n"); return -ENXIO; + } csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL); if (!csi) diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index 60498a5abebf..ab4f07e2e560 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -642,9 +642,6 @@ static void mgb4_remove(struct pci_dev *pdev) struct mgb4_dev *mgbdev = pci_get_drvdata(pdev); int i; -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(mgbdev->debugfs); -#endif #if IS_REACHABLE(CONFIG_HWMON) hwmon_device_unregister(mgbdev->hwmon_dev); #endif @@ -659,6 +656,10 @@ static void mgb4_remove(struct pci_dev *pdev) if (mgbdev->vin[i]) mgb4_vin_free(mgbdev->vin[i]); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(mgbdev->debugfs); +#endif + device_remove_groups(&mgbdev->pdev->dev, mgb4_pci_groups); free_spi(mgbdev); free_i2c(mgbdev); diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 6ad8002b22e1..99a75a59078e 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -409,7 +409,6 @@ struct ice_vsi { struct ice_tc_cfg tc_cfg; struct bpf_prog *xdp_prog; struct ice_tx_ring **xdp_rings; /* XDP ring array */ - unsigned long *af_xdp_zc_qps; /* tracks AF_XDP ZC enabled qps */ u16 num_xdp_txq; /* Used XDP queues */ u8 xdp_mapping_mode; /* ICE_MAP_MODE_[CONTIG|SCATTER] */ @@ -747,6 +746,25 @@ static inline void ice_set_ring_xdp(struct ice_tx_ring *ring) } /** + * ice_get_xp_from_qid - get ZC XSK buffer pool bound to a queue ID + * @vsi: pointer to VSI + * @qid: index of a queue to look at XSK buff pool presence + * + * Return: A pointer to xsk_buff_pool structure if there is a buffer pool + * attached and configured as zero-copy, NULL otherwise. + */ +static inline struct xsk_buff_pool *ice_get_xp_from_qid(struct ice_vsi *vsi, + u16 qid) +{ + struct xsk_buff_pool *pool = xsk_get_pool_from_qid(vsi->netdev, qid); + + if (!ice_is_xdp_ena_vsi(vsi)) + return NULL; + + return (pool && pool->dev) ? pool : NULL; +} + +/** * ice_xsk_pool - get XSK buffer pool bound to a ring * @ring: Rx ring to use * @@ -758,10 +776,7 @@ static inline struct xsk_buff_pool *ice_xsk_pool(struct ice_rx_ring *ring) struct ice_vsi *vsi = ring->vsi; u16 qid = ring->q_index; - if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps)) - return NULL; - - return xsk_get_pool_from_qid(vsi->netdev, qid); + return ice_get_xp_from_qid(vsi, qid); } /** @@ -786,12 +801,7 @@ static inline void ice_tx_xsk_pool(struct ice_vsi *vsi, u16 qid) if (!ring) return; - if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps)) { - ring->xsk_pool = NULL; - return; - } - - ring->xsk_pool = xsk_get_pool_from_qid(vsi->netdev, qid); + ring->xsk_pool = ice_get_xp_from_qid(vsi, qid); } /** @@ -920,9 +930,17 @@ int ice_down(struct ice_vsi *vsi); int ice_down_up(struct ice_vsi *vsi); int ice_vsi_cfg_lan(struct ice_vsi *vsi); struct ice_vsi *ice_lb_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi); + +enum ice_xdp_cfg { + ICE_XDP_CFG_FULL, /* Fully apply new config in .ndo_bpf() */ + ICE_XDP_CFG_PART, /* Save/use part of config in VSI rebuild */ +}; + int ice_vsi_determine_xdp_res(struct ice_vsi *vsi); -int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog); -int ice_destroy_xdp_rings(struct ice_vsi *vsi); +int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, + enum ice_xdp_cfg cfg_type); +int ice_destroy_xdp_rings(struct ice_vsi *vsi, enum ice_xdp_cfg cfg_type); +void ice_map_xdp_rings(struct ice_vsi *vsi); int ice_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, u32 flags); diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 687f6cb2b917..5d396c1a7731 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -842,6 +842,9 @@ void ice_vsi_map_rings_to_vectors(struct ice_vsi *vsi) } rx_rings_rem -= rx_rings_per_v; } + + if (ice_is_xdp_ena_vsi(vsi)) + ice_map_xdp_rings(vsi); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 5371e91f6bbb..7629b0190578 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -114,14 +114,8 @@ static int ice_vsi_alloc_arrays(struct ice_vsi *vsi) if (!vsi->q_vectors) goto err_vectors; - vsi->af_xdp_zc_qps = bitmap_zalloc(max_t(int, vsi->alloc_txq, vsi->alloc_rxq), GFP_KERNEL); - if (!vsi->af_xdp_zc_qps) - goto err_zc_qps; - return 0; -err_zc_qps: - devm_kfree(dev, vsi->q_vectors); err_vectors: devm_kfree(dev, vsi->rxq_map); err_rxq_map: @@ -309,8 +303,6 @@ static void ice_vsi_free_arrays(struct ice_vsi *vsi) dev = ice_pf_to_dev(pf); - bitmap_free(vsi->af_xdp_zc_qps); - vsi->af_xdp_zc_qps = NULL; /* free the ring and vector containers */ devm_kfree(dev, vsi->q_vectors); vsi->q_vectors = NULL; @@ -2282,22 +2274,23 @@ static int ice_vsi_cfg_def(struct ice_vsi *vsi) if (ret) goto unroll_vector_base; - ice_vsi_map_rings_to_vectors(vsi); - - /* Associate q_vector rings to napi */ - ice_vsi_set_napi_queues(vsi); - - vsi->stat_offsets_loaded = false; - if (ice_is_xdp_ena_vsi(vsi)) { ret = ice_vsi_determine_xdp_res(vsi); if (ret) goto unroll_vector_base; - ret = ice_prepare_xdp_rings(vsi, vsi->xdp_prog); + ret = ice_prepare_xdp_rings(vsi, vsi->xdp_prog, + ICE_XDP_CFG_PART); if (ret) goto unroll_vector_base; } + ice_vsi_map_rings_to_vectors(vsi); + + /* Associate q_vector rings to napi */ + ice_vsi_set_napi_queues(vsi); + + vsi->stat_offsets_loaded = false; + /* ICE_VSI_CTRL does not need RSS so skip RSS processing */ if (vsi->type != ICE_VSI_CTRL) /* Do not exit if configuring RSS had an issue, at @@ -2437,7 +2430,7 @@ void ice_vsi_decfg(struct ice_vsi *vsi) /* return value check can be skipped here, it always returns * 0 if reset is in progress */ - ice_destroy_xdp_rings(vsi); + ice_destroy_xdp_rings(vsi, ICE_XDP_CFG_PART); ice_vsi_clear_rings(vsi); ice_vsi_free_q_vectors(vsi); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index f60c022f7960..1b61ca3a6eb6 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -2707,17 +2707,72 @@ static void ice_vsi_assign_bpf_prog(struct ice_vsi *vsi, struct bpf_prog *prog) bpf_prog_put(old_prog); } +static struct ice_tx_ring *ice_xdp_ring_from_qid(struct ice_vsi *vsi, int qid) +{ + struct ice_q_vector *q_vector; + struct ice_tx_ring *ring; + + if (static_key_enabled(&ice_xdp_locking_key)) + return vsi->xdp_rings[qid % vsi->num_xdp_txq]; + + q_vector = vsi->rx_rings[qid]->q_vector; + ice_for_each_tx_ring(ring, q_vector->tx) + if (ice_ring_is_xdp(ring)) + return ring; + + return NULL; +} + +/** + * ice_map_xdp_rings - Map XDP rings to interrupt vectors + * @vsi: the VSI with XDP rings being configured + * + * Map XDP rings to interrupt vectors and perform the configuration steps + * dependent on the mapping. + */ +void ice_map_xdp_rings(struct ice_vsi *vsi) +{ + int xdp_rings_rem = vsi->num_xdp_txq; + int v_idx, q_idx; + + /* follow the logic from ice_vsi_map_rings_to_vectors */ + ice_for_each_q_vector(vsi, v_idx) { + struct ice_q_vector *q_vector = vsi->q_vectors[v_idx]; + int xdp_rings_per_v, q_id, q_base; + + xdp_rings_per_v = DIV_ROUND_UP(xdp_rings_rem, + vsi->num_q_vectors - v_idx); + q_base = vsi->num_xdp_txq - xdp_rings_rem; + + for (q_id = q_base; q_id < (q_base + xdp_rings_per_v); q_id++) { + struct ice_tx_ring *xdp_ring = vsi->xdp_rings[q_id]; + + xdp_ring->q_vector = q_vector; + xdp_ring->next = q_vector->tx.tx_ring; + q_vector->tx.tx_ring = xdp_ring; + } + xdp_rings_rem -= xdp_rings_per_v; + } + + ice_for_each_rxq(vsi, q_idx) { + vsi->rx_rings[q_idx]->xdp_ring = ice_xdp_ring_from_qid(vsi, + q_idx); + ice_tx_xsk_pool(vsi, q_idx); + } +} + /** * ice_prepare_xdp_rings - Allocate, configure and setup Tx rings for XDP * @vsi: VSI to bring up Tx rings used by XDP * @prog: bpf program that will be assigned to VSI + * @cfg_type: create from scratch or restore the existing configuration * * Return 0 on success and negative value on error */ -int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog) +int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog, + enum ice_xdp_cfg cfg_type) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; - int xdp_rings_rem = vsi->num_xdp_txq; struct ice_pf *pf = vsi->back; struct ice_qs_cfg xdp_qs_cfg = { .qs_mutex = &pf->avail_q_mutex, @@ -2730,8 +2785,7 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog) .mapping_mode = ICE_VSI_MAP_CONTIG }; struct device *dev; - int i, v_idx; - int status; + int status, i; dev = ice_pf_to_dev(pf); vsi->xdp_rings = devm_kcalloc(dev, vsi->num_xdp_txq, @@ -2750,49 +2804,15 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog) if (ice_xdp_alloc_setup_rings(vsi)) goto clear_xdp_rings; - /* follow the logic from ice_vsi_map_rings_to_vectors */ - ice_for_each_q_vector(vsi, v_idx) { - struct ice_q_vector *q_vector = vsi->q_vectors[v_idx]; - int xdp_rings_per_v, q_id, q_base; - - xdp_rings_per_v = DIV_ROUND_UP(xdp_rings_rem, - vsi->num_q_vectors - v_idx); - q_base = vsi->num_xdp_txq - xdp_rings_rem; - - for (q_id = q_base; q_id < (q_base + xdp_rings_per_v); q_id++) { - struct ice_tx_ring *xdp_ring = vsi->xdp_rings[q_id]; - - xdp_ring->q_vector = q_vector; - xdp_ring->next = q_vector->tx.tx_ring; - q_vector->tx.tx_ring = xdp_ring; - } - xdp_rings_rem -= xdp_rings_per_v; - } - - ice_for_each_rxq(vsi, i) { - if (static_key_enabled(&ice_xdp_locking_key)) { - vsi->rx_rings[i]->xdp_ring = vsi->xdp_rings[i % vsi->num_xdp_txq]; - } else { - struct ice_q_vector *q_vector = vsi->rx_rings[i]->q_vector; - struct ice_tx_ring *ring; - - ice_for_each_tx_ring(ring, q_vector->tx) { - if (ice_ring_is_xdp(ring)) { - vsi->rx_rings[i]->xdp_ring = ring; - break; - } - } - } - ice_tx_xsk_pool(vsi, i); - } - /* omit the scheduler update if in reset path; XDP queues will be * taken into account at the end of ice_vsi_rebuild, where * ice_cfg_vsi_lan is being called */ - if (ice_is_reset_in_progress(pf->state)) + if (cfg_type == ICE_XDP_CFG_PART) return 0; + ice_map_xdp_rings(vsi); + /* tell the Tx scheduler that right now we have * additional queues */ @@ -2842,22 +2862,21 @@ err_map_xdp: /** * ice_destroy_xdp_rings - undo the configuration made by ice_prepare_xdp_rings * @vsi: VSI to remove XDP rings + * @cfg_type: disable XDP permanently or allow it to be restored later * * Detach XDP rings from irq vectors, clean up the PF bitmap and free * resources */ -int ice_destroy_xdp_rings(struct ice_vsi *vsi) +int ice_destroy_xdp_rings(struct ice_vsi *vsi, enum ice_xdp_cfg cfg_type) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; struct ice_pf *pf = vsi->back; int i, v_idx; /* q_vectors are freed in reset path so there's no point in detaching - * rings; in case of rebuild being triggered not from reset bits - * in pf->state won't be set, so additionally check first q_vector - * against NULL + * rings */ - if (ice_is_reset_in_progress(pf->state) || !vsi->q_vectors[0]) + if (cfg_type == ICE_XDP_CFG_PART) goto free_qmap; ice_for_each_q_vector(vsi, v_idx) { @@ -2898,7 +2917,7 @@ free_qmap: if (static_key_enabled(&ice_xdp_locking_key)) static_branch_dec(&ice_xdp_locking_key); - if (ice_is_reset_in_progress(pf->state) || !vsi->q_vectors[0]) + if (cfg_type == ICE_XDP_CFG_PART) return 0; ice_vsi_assign_bpf_prog(vsi, NULL); @@ -3009,7 +3028,8 @@ ice_xdp_setup_prog(struct ice_vsi *vsi, struct bpf_prog *prog, if (xdp_ring_err) { NL_SET_ERR_MSG_MOD(extack, "Not enough Tx resources for XDP"); } else { - xdp_ring_err = ice_prepare_xdp_rings(vsi, prog); + xdp_ring_err = ice_prepare_xdp_rings(vsi, prog, + ICE_XDP_CFG_FULL); if (xdp_ring_err) NL_SET_ERR_MSG_MOD(extack, "Setting up XDP Tx resources failed"); } @@ -3020,7 +3040,7 @@ ice_xdp_setup_prog(struct ice_vsi *vsi, struct bpf_prog *prog, NL_SET_ERR_MSG_MOD(extack, "Setting up XDP Rx resources failed"); } else if (ice_is_xdp_ena_vsi(vsi) && !prog) { xdp_features_clear_redirect_target(vsi->netdev); - xdp_ring_err = ice_destroy_xdp_rings(vsi); + xdp_ring_err = ice_destroy_xdp_rings(vsi, ICE_XDP_CFG_FULL); if (xdp_ring_err) NL_SET_ERR_MSG_MOD(extack, "Freeing XDP Tx resources failed"); /* reallocate Rx queues that were used for zero-copy */ diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c index 84eab92dc03c..59e8879ac059 100644 --- a/drivers/net/ethernet/intel/ice/ice_nvm.c +++ b/drivers/net/ethernet/intel/ice/ice_nvm.c @@ -374,11 +374,25 @@ ice_read_nvm_module(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u1 * * Read the specified word from the copy of the Shadow RAM found in the * specified NVM module. + * + * Note that the Shadow RAM copy is always located after the CSS header, and + * is aligned to 64-byte (32-word) offsets. */ static int ice_read_nvm_sr_copy(struct ice_hw *hw, enum ice_bank_select bank, u32 offset, u16 *data) { - return ice_read_nvm_module(hw, bank, ICE_NVM_SR_COPY_WORD_OFFSET + offset, data); + u32 sr_copy; + + switch (bank) { + case ICE_ACTIVE_FLASH_BANK: + sr_copy = roundup(hw->flash.banks.active_css_hdr_len, 32); + break; + case ICE_INACTIVE_FLASH_BANK: + sr_copy = roundup(hw->flash.banks.inactive_css_hdr_len, 32); + break; + } + + return ice_read_nvm_module(hw, bank, sr_copy + offset, data); } /** @@ -440,8 +454,7 @@ int ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, u16 module_type) { - u16 pfa_len, pfa_ptr; - u16 next_tlv; + u16 pfa_len, pfa_ptr, next_tlv, max_tlv; int status; status = ice_read_sr_word(hw, ICE_SR_PFA_PTR, &pfa_ptr); @@ -454,11 +467,23 @@ ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, ice_debug(hw, ICE_DBG_INIT, "Failed to read PFA length.\n"); return status; } + + /* The Preserved Fields Area contains a sequence of Type-Length-Value + * structures which define its contents. The PFA length includes all + * of the TLVs, plus the initial length word itself, *and* one final + * word at the end after all of the TLVs. + */ + if (check_add_overflow(pfa_ptr, pfa_len - 1, &max_tlv)) { + dev_warn(ice_hw_to_dev(hw), "PFA starts at offset %u. PFA length of %u caused 16-bit arithmetic overflow.\n", + pfa_ptr, pfa_len); + return -EINVAL; + } + /* Starting with first TLV after PFA length, iterate through the list * of TLVs to find the requested one. */ next_tlv = pfa_ptr + 1; - while (next_tlv < pfa_ptr + pfa_len) { + while (next_tlv < max_tlv) { u16 tlv_sub_module_type; u16 tlv_len; @@ -482,10 +507,13 @@ ice_get_pfa_module_tlv(struct ice_hw *hw, u16 *module_tlv, u16 *module_tlv_len, } return -EINVAL; } - /* Check next TLV, i.e. current TLV pointer + length + 2 words - * (for current TLV's type and length) - */ - next_tlv = next_tlv + tlv_len + 2; + + if (check_add_overflow(next_tlv, 2, &next_tlv) || + check_add_overflow(next_tlv, tlv_len, &next_tlv)) { + dev_warn(ice_hw_to_dev(hw), "TLV of type %u and length 0x%04x caused 16-bit arithmetic overflow. The PFA starts at 0x%04x and has length of 0x%04x\n", + tlv_sub_module_type, tlv_len, pfa_ptr, pfa_len); + return -EINVAL; + } } /* Module does not exist */ return -ENOENT; @@ -1010,6 +1038,72 @@ static int ice_determine_active_flash_banks(struct ice_hw *hw) } /** + * ice_get_nvm_css_hdr_len - Read the CSS header length from the NVM CSS header + * @hw: pointer to the HW struct + * @bank: whether to read from the active or inactive flash bank + * @hdr_len: storage for header length in words + * + * Read the CSS header length from the NVM CSS header and add the Authentication + * header size, and then convert to words. + * + * Return: zero on success, or a negative error code on failure. + */ +static int +ice_get_nvm_css_hdr_len(struct ice_hw *hw, enum ice_bank_select bank, + u32 *hdr_len) +{ + u16 hdr_len_l, hdr_len_h; + u32 hdr_len_dword; + int status; + + status = ice_read_nvm_module(hw, bank, ICE_NVM_CSS_HDR_LEN_L, + &hdr_len_l); + if (status) + return status; + + status = ice_read_nvm_module(hw, bank, ICE_NVM_CSS_HDR_LEN_H, + &hdr_len_h); + if (status) + return status; + + /* CSS header length is in DWORD, so convert to words and add + * authentication header size + */ + hdr_len_dword = hdr_len_h << 16 | hdr_len_l; + *hdr_len = (hdr_len_dword * 2) + ICE_NVM_AUTH_HEADER_LEN; + + return 0; +} + +/** + * ice_determine_css_hdr_len - Discover CSS header length for the device + * @hw: pointer to the HW struct + * + * Determine the size of the CSS header at the start of the NVM module. This + * is useful for locating the Shadow RAM copy in the NVM, as the Shadow RAM is + * always located just after the CSS header. + * + * Return: zero on success, or a negative error code on failure. + */ +static int ice_determine_css_hdr_len(struct ice_hw *hw) +{ + struct ice_bank_info *banks = &hw->flash.banks; + int status; + + status = ice_get_nvm_css_hdr_len(hw, ICE_ACTIVE_FLASH_BANK, + &banks->active_css_hdr_len); + if (status) + return status; + + status = ice_get_nvm_css_hdr_len(hw, ICE_INACTIVE_FLASH_BANK, + &banks->inactive_css_hdr_len); + if (status) + return status; + + return 0; +} + +/** * ice_init_nvm - initializes NVM setting * @hw: pointer to the HW struct * @@ -1055,6 +1149,12 @@ int ice_init_nvm(struct ice_hw *hw) return status; } + status = ice_determine_css_hdr_len(hw); + if (status) { + ice_debug(hw, ICE_DBG_NVM, "Failed to determine Shadow RAM copy offsets.\n"); + return status; + } + status = ice_get_nvm_ver_info(hw, ICE_ACTIVE_FLASH_BANK, &flash->nvm); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to read NVM info.\n"); diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index f0796a93f428..eef397e5baa0 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -482,6 +482,8 @@ struct ice_bank_info { u32 orom_size; /* Size of OROM bank */ u32 netlist_ptr; /* Pointer to 1st Netlist bank */ u32 netlist_size; /* Size of Netlist bank */ + u32 active_css_hdr_len; /* Active CSS header length */ + u32 inactive_css_hdr_len; /* Inactive CSS header length */ enum ice_flash_bank nvm_bank; /* Active NVM bank */ enum ice_flash_bank orom_bank; /* Active OROM bank */ enum ice_flash_bank netlist_bank; /* Active Netlist bank */ @@ -1087,17 +1089,13 @@ struct ice_aq_get_set_rss_lut_params { #define ICE_SR_SECTOR_SIZE_IN_WORDS 0x800 /* CSS Header words */ +#define ICE_NVM_CSS_HDR_LEN_L 0x02 +#define ICE_NVM_CSS_HDR_LEN_H 0x03 #define ICE_NVM_CSS_SREV_L 0x14 #define ICE_NVM_CSS_SREV_H 0x15 -/* Length of CSS header section in words */ -#define ICE_CSS_HEADER_LENGTH 330 - -/* Offset of Shadow RAM copy in the NVM bank area. */ -#define ICE_NVM_SR_COPY_WORD_OFFSET roundup(ICE_CSS_HEADER_LENGTH, 32) - -/* Size in bytes of Option ROM trailer */ -#define ICE_NVM_OROM_TRAILER_LENGTH (2 * ICE_CSS_HEADER_LENGTH) +/* Length of Authentication header section in words */ +#define ICE_NVM_AUTH_HEADER_LEN 0x08 /* The Link Topology Netlist section is stored as a series of words. It is * stored in the NVM as a TLV, with the first two words containing the type diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index 7541f223bf4f..a65955eb23c0 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -269,7 +269,6 @@ static int ice_xsk_pool_disable(struct ice_vsi *vsi, u16 qid) if (!pool) return -EINVAL; - clear_bit(qid, vsi->af_xdp_zc_qps); xsk_pool_dma_unmap(pool, ICE_RX_DMA_ATTR); return 0; @@ -300,8 +299,6 @@ ice_xsk_pool_enable(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid) if (err) return err; - set_bit(qid, vsi->af_xdp_zc_qps); - return 0; } @@ -349,11 +346,13 @@ ice_realloc_rx_xdp_bufs(struct ice_rx_ring *rx_ring, bool pool_present) int ice_realloc_zc_buf(struct ice_vsi *vsi, bool zc) { struct ice_rx_ring *rx_ring; - unsigned long q; + uint i; + + ice_for_each_rxq(vsi, i) { + rx_ring = vsi->rx_rings[i]; + if (!rx_ring->xsk_pool) + continue; - for_each_set_bit(q, vsi->af_xdp_zc_qps, - max_t(int, vsi->alloc_txq, vsi->alloc_rxq)) { - rx_ring = vsi->rx_rings[q]; if (ice_realloc_rx_xdp_bufs(rx_ring, zc)) return -ENOMEM; } diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index f2c4f1966bb0..0cd2bd695db1 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1629,12 +1629,17 @@ static int igc_ethtool_get_eee(struct net_device *netdev, struct igc_hw *hw = &adapter->hw; u32 eeer; + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + edata->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + edata->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + edata->supported); + if (hw->dev_spec._base.eee_enable) mii_eee_cap1_mod_linkmode_t(edata->advertised, adapter->eee_advert); - *edata = adapter->eee; - eeer = rd32(IGC_EEER); /* EEE status on negotiated link */ diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 12f004f46082..305e05294a26 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -12,6 +12,7 @@ #include <linux/bpf_trace.h> #include <net/xdp_sock_drv.h> #include <linux/pci.h> +#include <linux/mdio.h> #include <net/ipv6.h> @@ -4975,6 +4976,9 @@ void igc_up(struct igc_adapter *adapter) /* start the watchdog. */ hw->mac.get_link_status = true; schedule_work(&adapter->watchdog_task); + + adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T | + MDIO_EEE_2_5GT; } /** diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index e8b73b9d75e3..97722ce8c4cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -2519,7 +2519,17 @@ static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc, * - when available free entries are less. * Lower priority ones out of avaialble free entries are always * chosen when 'high vs low' question arises. + * + * For a VF base MCAM match rule is set by its PF. And all the + * further MCAM rules installed by VF on its own are + * concatenated with the base rule set by its PF. Hence PF entries + * should be at lower priority compared to VF entries. Otherwise + * base rule is hit always and rules installed by VF will be of + * no use. Hence if the request is from PF then allocate low + * priority entries. */ + if (!(pcifunc & RVU_PFVF_FUNC_MASK)) + goto lprio_alloc; /* Get the search range for priority allocation request */ if (req->priority) { @@ -2528,17 +2538,6 @@ static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc, goto alloc; } - /* For a VF base MCAM match rule is set by its PF. And all the - * further MCAM rules installed by VF on its own are - * concatenated with the base rule set by its PF. Hence PF entries - * should be at lower priority compared to VF entries. Otherwise - * base rule is hit always and rules installed by VF will be of - * no use. Hence if the request is from PF and NOT a priority - * allocation request then allocate low priority entries. - */ - if (!(pcifunc & RVU_PFVF_FUNC_MASK)) - goto lprio_alloc; - /* Find out the search range for non-priority allocation request * * Get MCAM free entry count in middle zone. @@ -2568,6 +2567,18 @@ lprio_alloc: reverse = true; start = 0; end = mcam->bmap_entries; + /* Ensure PF requests are always at bottom and if PF requests + * for higher/lower priority entry wrt reference entry then + * honour that criteria and start search for entries from bottom + * and not in mid zone. + */ + if (!(pcifunc & RVU_PFVF_FUNC_MASK) && + req->priority == NPC_MCAM_HIGHER_PRIO) + end = req->ref_entry; + + if (!(pcifunc & RVU_PFVF_FUNC_MASK) && + req->priority == NPC_MCAM_LOWER_PRIO) + start = req->ref_entry; } alloc: diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index cae46290a7ae..c84ce54a84a0 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1131,9 +1131,9 @@ static int mtk_init_fq_dma(struct mtk_eth *eth) { const struct mtk_soc_data *soc = eth->soc; dma_addr_t phy_ring_tail; - int cnt = MTK_QDMA_RING_SIZE; + int cnt = soc->tx.fq_dma_size; dma_addr_t dma_addr; - int i; + int i, j, len; if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM)) eth->scratch_ring = eth->sram_base; @@ -1142,40 +1142,46 @@ static int mtk_init_fq_dma(struct mtk_eth *eth) cnt * soc->tx.desc_size, ð->phy_scratch_ring, GFP_KERNEL); + if (unlikely(!eth->scratch_ring)) return -ENOMEM; - eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); - if (unlikely(!eth->scratch_head)) - return -ENOMEM; + phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1); - dma_addr = dma_map_single(eth->dma_dev, - eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) - return -ENOMEM; + for (j = 0; j < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) { + len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH); + eth->scratch_head[j] = kcalloc(len, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); - phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1); + if (unlikely(!eth->scratch_head[j])) + return -ENOMEM; - for (i = 0; i < cnt; i++) { - dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE; - struct mtk_tx_dma_v2 *txd; + dma_addr = dma_map_single(eth->dma_dev, + eth->scratch_head[j], len * MTK_QDMA_PAGE_SIZE, + DMA_FROM_DEVICE); - txd = eth->scratch_ring + i * soc->tx.desc_size; - txd->txd1 = addr; - if (i < cnt - 1) - txd->txd2 = eth->phy_scratch_ring + - (i + 1) * soc->tx.desc_size; + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; - txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); - if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA)) - txd->txd3 |= TX_DMA_PREP_ADDR64(addr); - txd->txd4 = 0; - if (mtk_is_netsys_v2_or_greater(eth)) { - txd->txd5 = 0; - txd->txd6 = 0; - txd->txd7 = 0; - txd->txd8 = 0; + for (i = 0; i < cnt; i++) { + struct mtk_tx_dma_v2 *txd; + + txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size; + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (j * MTK_FQ_DMA_LENGTH + i < cnt) + txd->txd2 = eth->phy_scratch_ring + + (j * MTK_FQ_DMA_LENGTH + i + 1) * soc->tx.desc_size; + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA)) + txd->txd3 |= TX_DMA_PREP_ADDR64(dma_addr + i * MTK_QDMA_PAGE_SIZE); + + txd->txd4 = 0; + if (mtk_is_netsys_v2_or_greater(eth)) { + txd->txd5 = 0; + txd->txd6 = 0; + txd->txd7 = 0; + txd->txd8 = 0; + } } } @@ -2457,7 +2463,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth) if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ring_size = MTK_QDMA_RING_SIZE; else - ring_size = MTK_DMA_SIZE; + ring_size = soc->tx.dma_size; ring->buf = kcalloc(ring_size, sizeof(*ring->buf), GFP_KERNEL); @@ -2465,8 +2471,8 @@ static int mtk_tx_alloc(struct mtk_eth *eth) goto no_tx_mem; if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) { - ring->dma = eth->sram_base + ring_size * sz; - ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz; + ring->dma = eth->sram_base + soc->tx.fq_dma_size * sz; + ring->phys = eth->phy_scratch_ring + soc->tx.fq_dma_size * (dma_addr_t)sz; } else { ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz, &ring->phys, GFP_KERNEL); @@ -2588,6 +2594,7 @@ static void mtk_tx_clean(struct mtk_eth *eth) static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) { const struct mtk_reg_map *reg_map = eth->soc->reg_map; + const struct mtk_soc_data *soc = eth->soc; struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size, tx_ring_size; int i; @@ -2595,7 +2602,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) tx_ring_size = MTK_QDMA_RING_SIZE; else - tx_ring_size = MTK_DMA_SIZE; + tx_ring_size = soc->tx.dma_size; if (rx_flag == MTK_RX_FLAGS_QDMA) { if (ring_no) @@ -2610,7 +2617,7 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) rx_dma_size = MTK_HW_LRO_DMA_SIZE; } else { rx_data_len = ETH_DATA_LEN; - rx_dma_size = MTK_DMA_SIZE; + rx_dma_size = soc->rx.dma_size; } ring->frag_size = mtk_max_frag_size(rx_data_len); @@ -3139,7 +3146,10 @@ static void mtk_dma_free(struct mtk_eth *eth) mtk_rx_clean(eth, ð->rx_ring[i], false); } - kfree(eth->scratch_head); + for (i = 0; i < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); i++) { + kfree(eth->scratch_head[i]); + eth->scratch_head[i] = NULL; + } } static bool mtk_hw_reset_check(struct mtk_eth *eth) @@ -5052,11 +5062,14 @@ static const struct mtk_soc_data mt2701_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5076,11 +5089,14 @@ static const struct mtk_soc_data mt7621_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5102,11 +5118,14 @@ static const struct mtk_soc_data mt7622_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5127,11 +5146,14 @@ static const struct mtk_soc_data mt7623_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5150,11 +5172,14 @@ static const struct mtk_soc_data mt7629_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), .irq_done_mask = MTK_RX_DONE_INT, .dma_l4_valid = RX_DMA_L4_VALID, + .dma_size = MTK_DMA_SIZE(2K), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, }, @@ -5176,6 +5201,8 @@ static const struct mtk_soc_data mt7981_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5183,6 +5210,7 @@ static const struct mtk_soc_data mt7981_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5202,6 +5230,8 @@ static const struct mtk_soc_data mt7986_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5209,6 +5239,7 @@ static const struct mtk_soc_data mt7986_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5228,6 +5259,8 @@ static const struct mtk_soc_data mt7988_data = { .desc_size = sizeof(struct mtk_tx_dma_v2), .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), + .fq_dma_size = MTK_DMA_SIZE(4K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma_v2), @@ -5235,6 +5268,7 @@ static const struct mtk_soc_data mt7988_data = { .dma_l4_valid = RX_DMA_L4_VALID_V2, .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, .dma_len_offset = 8, + .dma_size = MTK_DMA_SIZE(2K), }, }; @@ -5249,6 +5283,7 @@ static const struct mtk_soc_data rt5350_data = { .desc_size = sizeof(struct mtk_tx_dma), .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, .rx = { .desc_size = sizeof(struct mtk_rx_dma), @@ -5256,6 +5291,7 @@ static const struct mtk_soc_data rt5350_data = { .dma_l4_valid = RX_DMA_L4_VALID_PDMA, .dma_max_len = MTK_TX_DMA_BUF_LEN, .dma_len_offset = 16, + .dma_size = MTK_DMA_SIZE(2K), }, }; diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 4eab30b44070..f5174f6cb1bb 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -32,7 +32,9 @@ #define MTK_TX_DMA_BUF_LEN 0x3fff #define MTK_TX_DMA_BUF_LEN_V2 0xffff #define MTK_QDMA_RING_SIZE 2048 -#define MTK_DMA_SIZE 512 +#define MTK_DMA_SIZE(x) (SZ_##x) +#define MTK_FQ_DMA_HEAD 32 +#define MTK_FQ_DMA_LENGTH 2048 #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) #define MTK_DMA_DUMMY_DESC 0xffffffff @@ -1176,6 +1178,8 @@ struct mtk_soc_data { u32 desc_size; u32 dma_max_len; u32 dma_len_offset; + u32 dma_size; + u32 fq_dma_size; } tx; struct { u32 desc_size; @@ -1183,6 +1187,7 @@ struct mtk_soc_data { u32 dma_l4_valid; u32 dma_max_len; u32 dma_len_offset; + u32 dma_size; } rx; }; @@ -1264,7 +1269,7 @@ struct mtk_eth { struct napi_struct rx_napi; void *scratch_ring; dma_addr_t phy_scratch_ring; - void *scratch_head; + void *scratch_head[MTK_FQ_DMA_HEAD]; struct clk *clks[MTK_CLK_MAX]; struct mii_bus *mii_bus; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 2d95a9b7b44e..b61b7d966114 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -373,6 +373,10 @@ int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev) do { if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for NIC IFC\n"); + return -EACCES; + } cond_resched(); } while (!time_after(jiffies, end)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index ad38e31822df..a6329ca2d9bf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -248,6 +248,10 @@ recover_from_sw_reset: do { if (mlx5_get_nic_state(dev) == MLX5_INITIAL_SEG_NIC_INTERFACE_DISABLED) break; + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for NIC IFC\n"); + goto unlock; + } msleep(20); } while (!time_after(jiffies, end)); @@ -317,6 +321,10 @@ int mlx5_health_wait_pci_up(struct mlx5_core_dev *dev) mlx5_core_warn(dev, "device is being removed, stop waiting for PCI\n"); return -ENODEV; } + if (pci_channel_offline(dev->pdev)) { + mlx5_core_err(dev, "PCI channel offline, stop waiting for PCI\n"); + return -EACCES; + } msleep(100); } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c index c16b462ddedf..ab2717012b79 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c @@ -88,9 +88,13 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev, &dest, 1); if (IS_ERR(lag_definer->rules[idx])) { err = PTR_ERR(lag_definer->rules[idx]); - while (i--) - while (j--) + do { + while (j--) { + idx = i * ldev->buckets + j; mlx5_del_flow_rules(lag_definer->rules[idx]); + } + j = ldev->buckets; + } while (i--); goto destroy_fg; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c index 6b774e0c2766..d0b595ba6110 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/pci_vsc.c @@ -74,6 +74,10 @@ int mlx5_vsc_gw_lock(struct mlx5_core_dev *dev) ret = -EBUSY; goto pci_unlock; } + if (pci_channel_offline(dev->pdev)) { + ret = -EACCES; + goto pci_unlock; + } /* Check if semaphore is already locked */ ret = vsc_read(dev, VSC_SEMAPHORE_OFFSET, &lock_val); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 6574c145dc1e..459a836a5d9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1298,6 +1298,9 @@ static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot) if (!err) mlx5_function_disable(dev, boot); + else + mlx5_stop_health_poll(dev, boot); + return err; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 5dba6d2d633c..2427610f4306 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -586,6 +586,7 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err); goto out_xdp_abort; } + buf_info->page = NULL; stats->xdp_tx++; /* the Tx completion will free the buffers */ diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2b8f8b7f1517..5aada7cf3da7 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -866,6 +866,17 @@ static int ksz8061_config_init(struct phy_device *phydev) { int ret; + /* Chip can be powered down by the bootstrap code. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (ret & BMCR_PDOWN) { + ret = phy_write(phydev, MII_BMCR, ret & ~BMCR_PDOWN); + if (ret < 0) + return ret; + usleep_range(1000, 2000); + } + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); if (ret) return ret; @@ -1939,7 +1950,7 @@ static const struct ksz9477_errata_write ksz9477_errata_writes[] = { {0x1c, 0x20, 0xeeee}, }; -static int ksz9477_config_init(struct phy_device *phydev) +static int ksz9477_phy_errata(struct phy_device *phydev) { int err; int i; @@ -1967,16 +1978,30 @@ static int ksz9477_config_init(struct phy_device *phydev) return err; } + err = genphy_restart_aneg(phydev); + if (err) + return err; + + return err; +} + +static int ksz9477_config_init(struct phy_device *phydev) +{ + int err; + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + err = ksz9477_phy_errata(phydev); + if (err) + return err; + } + /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes * in this switch shall be regarded as broken. */ if (phydev->dev_flags & MICREL_NO_EEE) phydev->eee_broken_modes = -1; - err = genphy_restart_aneg(phydev); - if (err) - return err; - return kszphy_config_init(phydev); } @@ -2085,6 +2110,71 @@ static int kszphy_resume(struct phy_device *phydev) return 0; } +static int ksz9477_resume(struct phy_device *phydev) +{ + int ret; + + /* No need to initialize registers if not powered down. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + + /* After switching from power-down to normal mode, an internal global + * reset is automatically generated. Wait a minimum of 1 ms before + * read/write access to the PHY registers. + */ + usleep_range(1000, 2000); + + /* Only KSZ9897 family of switches needs this fix. */ + if ((phydev->phy_id & 0xf) == 1) { + ret = ksz9477_phy_errata(phydev); + if (ret) + return ret; + } + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + +static int ksz8061_resume(struct phy_device *phydev) +{ + int ret; + + /* This function can be called twice when the Ethernet device is on. */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (!(ret & BMCR_PDOWN)) + return 0; + + genphy_resume(phydev); + usleep_range(1000, 2000); + + /* Re-program the value after chip is reset. */ + ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); + if (ret) + return ret; + + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + if (phydev->drv->config_intr) + phydev->drv->config_intr(phydev); + } + + return 0; +} + static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -5339,7 +5429,7 @@ static struct phy_driver ksphy_driver[] = { .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = kszphy_suspend, - .resume = kszphy_resume, + .resume = ksz8061_resume, }, { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, @@ -5493,7 +5583,7 @@ static struct phy_driver ksphy_driver[] = { .config_intr = kszphy_config_intr, .handle_interrupt = kszphy_handle_interrupt, .suspend = genphy_suspend, - .resume = genphy_resume, + .resume = ksz9477_resume, .get_features = ksz9477_get_features, } }; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4a802c0ea2cb..61a57d134544 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2686,6 +2686,7 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd { struct scatterlist *sgs[5], hdr, stat; u32 out_num = 0, tmp, in_num = 0; + bool ok; int ret; /* Caller should know better */ @@ -2731,8 +2732,9 @@ static bool virtnet_send_command_reply(struct virtnet_info *vi, u8 class, u8 cmd } unlock: + ok = vi->ctrl->status == VIRTIO_NET_OK; mutex_unlock(&vi->cvq_lock); - return vi->ctrl->status == VIRTIO_NET_OK; + return ok; } static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, @@ -4257,7 +4259,6 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, struct virtio_net_ctrl_coal_rx *coal_rx __free(kfree) = NULL; bool rx_ctrl_dim_on = !!ec->use_adaptive_rx_coalesce; struct scatterlist sgs_rx; - int ret = 0; int i; if (rx_ctrl_dim_on && !virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) @@ -4267,27 +4268,27 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, ec->rx_max_coalesced_frames != vi->intr_coal_rx.max_packets)) return -EINVAL; - /* Acquire all queues dim_locks */ - for (i = 0; i < vi->max_queue_pairs; i++) - mutex_lock(&vi->rq[i].dim_lock); - if (rx_ctrl_dim_on && !vi->rx_dim_enabled) { vi->rx_dim_enabled = true; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].dim_enabled = true; - goto unlock; + mutex_unlock(&vi->rq[i].dim_lock); + } + return 0; } coal_rx = kzalloc(sizeof(*coal_rx), GFP_KERNEL); - if (!coal_rx) { - ret = -ENOMEM; - goto unlock; - } + if (!coal_rx) + return -ENOMEM; if (!rx_ctrl_dim_on && vi->rx_dim_enabled) { vi->rx_dim_enabled = false; - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].dim_enabled = false; + mutex_unlock(&vi->rq[i].dim_lock); + } } /* Since the per-queue coalescing params can be set, @@ -4300,22 +4301,19 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi, if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, - &sgs_rx)) { - ret = -EINVAL; - goto unlock; - } + &sgs_rx)) + return -EINVAL; vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs; vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames; for (i = 0; i < vi->max_queue_pairs; i++) { + mutex_lock(&vi->rq[i].dim_lock); vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs; vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames; - } -unlock: - for (i = vi->max_queue_pairs - 1; i >= 0; i--) mutex_unlock(&vi->rq[i].dim_lock); + } - return ret; + return 0; } static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, @@ -4417,9 +4415,9 @@ static void virtnet_rx_dim_work(struct work_struct *work) if (err) pr_debug("%s: Failed to send dim parameters on rxq%d\n", dev->name, qnum); - dim->state = DIM_START_MEASURE; } out: + dim->state = DIM_START_MEASURE; mutex_unlock(&rq->dim_lock); } diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 89ca6e75fcc6..63822d454c00 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2034,8 +2034,8 @@ vmxnet3_rq_destroy_all_rxdataring(struct vmxnet3_adapter *adapter) rq->data_ring.base, rq->data_ring.basePA); rq->data_ring.base = NULL; - rq->data_ring.desc_size = 0; } + rq->data_ring.desc_size = 0; } } diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index f78dd0438843..567cb3faab70 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1446,6 +1446,10 @@ static bool vxlan_snoop(struct net_device *dev, struct vxlan_fdb *f; u32 ifindex = 0; + /* Ignore packets from invalid src-address */ + if (!is_valid_ether_addr(src_mac)) + return true; + #if IS_ENABLED(CONFIG_IPV6) if (src_ip->sa.sa_family == AF_INET6 && (ipv6_addr_type(&src_ip->sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)) @@ -1616,10 +1620,6 @@ static bool vxlan_set_mac(struct vxlan_dev *vxlan, if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr)) return false; - /* Ignore packets from invalid src-address */ - if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) - return false; - /* Get address from the outer IP header */ if (vxlan_get_sk_family(vs) == AF_INET) { saddr.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index e6ea884cafc1..4f385f4a8cef 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -45,6 +45,7 @@ config ATH10K_SNOC depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_SMEM + depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n select QCOM_SCM select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 3cc817a3b4a4..b82e8fb28541 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -604,7 +604,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = true, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 16 + 1, + .num_vdevs = 3, .num_peers = 512, .supports_suspend = false, .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 4f62e38ba48b..9b96dbb21d83 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -7988,8 +7988,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_base *ab = ar->ab; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); int ret; - struct cur_regulatory_info *reg_info; - enum ieee80211_ap_reg_power power_type; mutex_lock(&ar->conf_mutex); @@ -8000,17 +7998,6 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, if (ath11k_wmi_supports_6ghz_cc_ext(ar) && ctx->def.chan->band == NL80211_BAND_6GHZ && arvif->vdev_type == WMI_VDEV_TYPE_STA) { - reg_info = &ab->reg_info_store[ar->pdev_idx]; - power_type = vif->bss_conf.power_type; - - ath11k_dbg(ab, ATH11K_DBG_MAC, "chanctx power type %d\n", power_type); - - if (power_type == IEEE80211_REG_UNSET_AP) { - ret = -EINVAL; - goto out; - } - - ath11k_reg_handle_chan_list(ab, reg_info, power_type); arvif->chanctx = *ctx; ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); } @@ -9626,6 +9613,8 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + enum ieee80211_ap_reg_power power_type; + struct cur_regulatory_info *reg_info; struct ath11k_peer *peer; int ret = 0; @@ -9705,6 +9694,29 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", sta->addr, arvif->vdev_id, ret); } + + if (!ret && + ath11k_wmi_supports_6ghz_cc_ext(ar) && + arvif->vdev_type == WMI_VDEV_TYPE_STA && + arvif->chanctx.def.chan && + arvif->chanctx.def.chan->band == NL80211_BAND_6GHZ) { + reg_info = &ar->ab->reg_info_store[ar->pdev_idx]; + power_type = vif->bss_conf.power_type; + + if (power_type == IEEE80211_REG_UNSET_AP) { + ath11k_warn(ar->ab, "invalid power type %d\n", + power_type); + ret = -EINVAL; + } else { + ret = ath11k_reg_handle_chan_list(ar->ab, + reg_info, + power_type); + if (ret) + ath11k_warn(ar->ab, + "failed to handle chan list with power type %d\n", + power_type); + } + } } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { spin_lock_bh(&ar->ab->base_lock); diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 79eb3f9c902f..debe7c5919ef 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -561,6 +561,7 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) { int i, j, n, ret, num_vectors = 0; u32 user_base_data = 0, base_vector = 0; + struct ath11k_ext_irq_grp *irq_grp; unsigned long irq_flags; ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, @@ -574,14 +575,16 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) irq_flags |= IRQF_NOBALANCING; for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { - struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + irq_grp = &ab->ext_irq_grp[i]; u32 num_irq = 0; irq_grp->ab = ab; irq_grp->grp_id = i; irq_grp->napi_ndev = alloc_netdev_dummy(0); - if (!irq_grp->napi_ndev) - return -ENOMEM; + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + goto fail_allocate; + } netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath11k_pcic_ext_grp_napi_poll); @@ -606,11 +609,8 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) int irq = ath11k_pcic_get_msi_irq(ab, vector); if (irq < 0) { - for (n = 0; n <= i; n++) { - irq_grp = &ab->ext_irq_grp[n]; - free_netdev(irq_grp->napi_ndev); - } - return irq; + ret = irq; + goto fail_irq; } ab->irq_num[irq_idx] = irq; @@ -635,6 +635,15 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) } return 0; +fail_irq: + /* i ->napi_ndev was properly allocated. Free it also */ + i += 1; +fail_allocate: + for (n = 0; n < i; n++) { + irq_grp = &ab->ext_irq_grp[n]; + free_netdev(irq_grp->napi_ndev); + } + return ret; } int ath11k_pcic_config_irq(struct ath11k_base *ab) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 33654f228ee8..d156a9c64194 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1815,8 +1815,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) err_fw: #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(drv->dbgfs_drv); - iwl_dbg_tlv_free(drv->trans); #endif + iwl_dbg_tlv_free(drv->trans); kfree(drv); err: return ERR_PTR(ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 71e6b06481a9..54f4acbbd05b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -595,6 +595,12 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, void *_data) { struct wowlan_key_gtk_type_iter *data = _data; + __le32 *cipher = NULL; + + if (key->keyidx == 4 || key->keyidx == 5) + cipher = &data->kek_kck_cmd->igtk_cipher; + if (key->keyidx == 6 || key->keyidx == 7) + cipher = &data->kek_kck_cmd->bigtk_cipher; switch (key->cipher) { default: @@ -606,10 +612,13 @@ static void iwl_mvm_wowlan_gtk_type_iter(struct ieee80211_hw *hw, return; case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_GMAC_128: - data->kek_kck_cmd->igtk_cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + if (cipher) + *cipher = cpu_to_le32(STA_KEY_FLG_GCMP); return; case WLAN_CIPHER_SUITE_AES_CMAC: - data->kek_kck_cmd->igtk_cipher = cpu_to_le32(STA_KEY_FLG_CCM); + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + if (cipher) + *cipher = cpu_to_le32(STA_KEY_FLG_CCM); return; case WLAN_CIPHER_SUITE_CCMP: if (!sta) @@ -2341,7 +2350,8 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, out: if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, - WOWLAN_GET_STATUSES, 0) < 10) { + WOWLAN_GET_STATUSES, + IWL_FW_CMD_VER_UNKNOWN) < 10) { mvmvif->seqno_valid = true; /* +0x10 because the set API expects next-to-use, not last-used */ mvmvif->seqno = status->non_qos_seq_ctr + 0x10; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 79f4ac8cbc72..8101ecbb478b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1617,6 +1617,15 @@ static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len) &beacon_cmd.tim_size, beacon->data, beacon->len); + if (iwl_fw_lookup_cmd_ver(mvm->fw, + BEACON_TEMPLATE_CMD, 0) >= 14) { + u32 offset = iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len); + + beacon_cmd.btwt_offset = cpu_to_le32(offset); + } + iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd, sizeof(beacon_cmd)); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e7f5978ef2d7..f4937a100cbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -94,20 +94,10 @@ void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mfu_assert_dump_notif *mfu_dump_notif = (void *)pkt->data; - __le32 *dump_data = mfu_dump_notif->data; - int n_words = le32_to_cpu(mfu_dump_notif->data_size) / sizeof(__le32); - int i; if (mfu_dump_notif->index_num == 0) IWL_INFO(mvm, "MFUART assert id 0x%x occurred\n", le32_to_cpu(mfu_dump_notif->assert_id)); - - for (i = 0; i < n_words; i++) - IWL_DEBUG_INFO(mvm, - "MFUART assert dump, dword %u: 0x%08x\n", - le16_to_cpu(mfu_dump_notif->index_num) * - n_words + i, - le32_to_cpu(dump_data[i])); } static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, @@ -895,8 +885,8 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) int ret; u16 len = 0; u32 n_subbands; - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, - IWL_FW_CMD_VER_UNKNOWN); + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3); + if (cmd_ver >= 7) { len = sizeof(cmd.v7); n_subbands = IWL_NUM_SUB_BANDS_V2; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 5a06f887769a..5144fa0f96b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -873,7 +873,7 @@ void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, } } -static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) +u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) { struct ieee80211_mgmt *mgmt = (void *)beacon; const u8 *ie; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 486a6b8f3c97..de9f0b446545 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1128,6 +1128,39 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); } +static void iwl_mvm_cleanup_sta_iterator(void *data, struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = data; + struct iwl_mvm_sta *mvm_sta; + struct ieee80211_vif *vif; + int link_id; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + vif = mvm_sta->vif; + + if (!sta->valid_links) + return; + + for (link_id = 0; link_id < ARRAY_SIZE((sta)->link); link_id++) { + struct iwl_mvm_link_sta *mvm_link_sta; + + mvm_link_sta = + rcu_dereference_check(mvm_sta->link[link_id], + lockdep_is_held(&mvm->mutex)); + if (mvm_link_sta && !(vif->active_links & BIT(link_id))) { + /* + * We have a link STA but the link is inactive in + * mac80211. This will happen if we failed to + * deactivate the link but mac80211 roll back the + * deactivation of the link. + * Delete the stale data to avoid issues later on. + */ + iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_link_sta, + link_id, false); + } + } +} + static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { iwl_mvm_stop_device(mvm); @@ -1150,6 +1183,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) */ ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); + /* cleanup stations as links may be gone after restart */ + ieee80211_iterate_stations_atomic(mvm->hw, + iwl_mvm_cleanup_sta_iterator, mvm); + mvm->p2p_device_vif = NULL; iwl_mvm_reset_phy_ctxts(mvm); @@ -6348,7 +6385,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, .len[0] = sizeof(cmd), .data[1] = data, .len[1] = size, - .flags = sync ? 0 : CMD_ASYNC, + .flags = CMD_SEND_IN_RFKILL | (sync ? 0 : CMD_ASYNC), }; int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 0a3b7284eedd..fcfd2dd7568e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -75,8 +75,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, goto out_free_bf; iwl_mvm_tcm_add_vif(mvm, vif); - INIT_DELAYED_WORK(&mvmvif->csa_work, - iwl_mvm_channel_switch_disconnect_wk); if (vif->type == NL80211_IFTYPE_MONITOR) { mvm->monitor_on = true; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index b7a461dba41e..9d139b56e152 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -515,11 +515,11 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return iwl_mvm_mld_send_sta_cmd(mvm, &cmd); } -static void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvm_sta, - struct iwl_mvm_link_sta *mvm_sta_link, - unsigned int link_id, - bool is_in_fw) +void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + struct iwl_mvm_link_sta *mvm_sta_link, + unsigned int link_id, + bool is_in_fw) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta_link->sta_id], is_in_fw ? ERR_PTR(-EINVAL) : NULL); @@ -1014,7 +1014,8 @@ static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm, cmd.modify.tid = cpu_to_le32(data->tid); - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL, + sizeof(cmd), &cmd); data->sta_mask = new_sta_mask; if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1f58c727fa63..0a1959bd4079 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1758,6 +1758,7 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type, u32 *gp2, u64 *boottime, ktime_t *realtime); u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); +u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size); /* Tx / Host Commands */ int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index 376b23b409dc..6cd4ec4d8f34 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -122,13 +122,8 @@ enum { #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -/* - * FIXME - various places in firmware API still use u8, - * e.g. LQ command and SCD config command. - * This should be 256 instead. - */ -#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF (255) -#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_MAX (255) +#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF (64) +#define LINK_QUAL_AGG_FRAME_LIMIT_GEN2_MAX (64) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) #define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d78af2928152..489cfb0a4ab1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2450,8 +2450,11 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, * * We mark it as mac header, for upper layers to know where * all radio tap header ends. + * + * Since data doesn't move data while putting data on skb and that is + * the only way we use, data + len is the next place that hdr would be put */ - skb_reset_mac_header(skb); + skb_set_mac_header(skb, skb->len); /* * Override the nss from the rx_vec since the rate_n_flags has diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index a7ec172eeade..b5f664ae5a17 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1313,7 +1313,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, if (IWL_MVM_ADWELL_MAX_BUDGET) cmd->v7.adwell_max_budget = cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); - else if (params->ssids && params->ssids[0].ssid_len) + else if (params->n_ssids && params->ssids[0].ssid_len) cmd->v7.adwell_max_budget = cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); else @@ -1418,7 +1418,7 @@ iwl_mvm_scan_umac_dwell_v11(struct iwl_mvm *mvm, if (IWL_MVM_ADWELL_MAX_BUDGET) general_params->adwell_max_budget = cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); - else if (params->ssids && params->ssids[0].ssid_len) + else if (params->n_ssids && params->ssids[0].ssid_len) general_params->adwell_max_budget = cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); else @@ -1730,7 +1730,10 @@ iwl_mvm_umac_scan_fill_6g_chan_list(struct iwl_mvm *mvm, break; } - if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE) { + if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE && + !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid), + "scan: invalid BSSID at index %u, index_b=%u\n", + j, idex_b)) { memcpy(&pp->bssid_array[idex_b++], scan_6ghz_params[j].bssid, ETH_ALEN); } @@ -3319,10 +3322,11 @@ static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(IWL_ALWAYS_LONG_GROUP, SCAN_ABORT_UMAC), - 0, sizeof(cmd), &cmd); + CMD_SEND_IN_RFKILL, sizeof(cmd), &cmd); if (!ret) mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; + IWL_DEBUG_SCAN(mvm, "Scan abort: ret=%d\n", ret); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 20d4968d692a..cc79fe991c26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2848,7 +2848,12 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, .action = start ? cpu_to_le32(IWL_RX_BAID_ACTION_ADD) : cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE), }; - u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD), + .flags = CMD_SEND_IN_RFKILL, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; int ret; BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); @@ -2860,7 +2865,7 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, cmd.alloc.ssn = cpu_to_le16(ssn); cmd.alloc.win_size = cpu_to_le16(buf_size); baid = -EIO; - } else if (iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 1) == 1) { + } else if (iwl_fw_lookup_cmd_ver(mvm->fw, hcmd.id, 1) == 1) { cmd.remove_v1.baid = cpu_to_le32(baid); BUILD_BUG_ON(sizeof(cmd.remove_v1) > sizeof(cmd.remove)); } else { @@ -2869,8 +2874,7 @@ static int iwl_mvm_fw_baid_op_cmd(struct iwl_mvm *mvm, cmd.remove.tid = cpu_to_le32(tid); } - ret = iwl_mvm_send_cmd_pdu_status(mvm, cmd_id, sizeof(cmd), - &cmd, &baid); + ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &baid); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 264f1f9394b6..754a05a8c189 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -662,6 +662,11 @@ int iwl_mvm_mld_update_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int iwl_mvm_mld_rm_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + struct iwl_mvm_link_sta *mvm_sta_link, + unsigned int link_id, + bool is_in_fw); int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id); int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 0971c164b57e..c27acaf0eb1c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -1326,6 +1326,10 @@ static void mt7615_set_rekey_data(struct ieee80211_hw *hw, #endif /* CONFIG_PM */ const struct ieee80211_ops mt7615_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, .tx = mt7615_tx, .start = mt7615_start, .stop = mt7615_stop, diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index 7d9fb9f2d527..089102ed9ae5 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -237,11 +237,12 @@ static int set_channel(struct wiphy *wiphy, struct wilc_vif *vif; u32 channelnum; int result; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return PTR_ERR(vif); } @@ -252,7 +253,7 @@ static int set_channel(struct wiphy *wiphy, if (result) netdev_err(vif->ndev, "Error in setting channel\n"); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return result; } @@ -805,8 +806,9 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; struct wilc_priv *priv; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) goto out; @@ -861,7 +863,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed) netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n"); out: - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1537,19 +1539,20 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, if (type == NL80211_IFTYPE_MONITOR) { struct net_device *ndev; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_vif_from_type(wl, WILC_AP_MODE); if (!vif) { vif = wilc_get_vif_from_type(wl, WILC_GO_MODE); if (!vif) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; } } if (vif->monitor_flag) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; } @@ -1557,12 +1560,12 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, if (ndev) { vif->monitor_flag = 1; } else { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ERR_PTR(-EINVAL); } wdev = &vif->priv.wdev; - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return wdev; } @@ -1610,7 +1613,7 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) list_del_rcu(&vif->list); wl->vif_num--; mutex_unlock(&wl->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wl->srcu); return 0; } @@ -1635,23 +1638,25 @@ static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled) { struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return; } netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled); wilc_set_wowlan_trigger(vif, enabled); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); } static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { int ret; + int srcu_idx; s32 tx_power = MBM_TO_DBM(mbm); struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; @@ -1659,10 +1664,10 @@ static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, if (!wl->initialized) return -EIO; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); vif = wilc_get_wl_to_vif(wl); if (IS_ERR(vif)) { - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return -EINVAL; } @@ -1674,7 +1679,7 @@ static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, ret = wilc_set_tx_power(vif, tx_power); if (ret) netdev_err(vif->ndev, "Failed to set tx power\n"); - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return ret; } @@ -1757,6 +1762,7 @@ static void wlan_init_locks(struct wilc *wl) init_completion(&wl->cfg_event); init_completion(&wl->sync_event); init_completion(&wl->txq_thread_started); + init_srcu_struct(&wl->srcu); } void wlan_deinit_locks(struct wilc *wilc) @@ -1767,6 +1773,7 @@ void wlan_deinit_locks(struct wilc *wilc) mutex_destroy(&wilc->txq_add_to_head_cs); mutex_destroy(&wilc->vif_mutex); mutex_destroy(&wilc->deinit_lock); + cleanup_srcu_struct(&wilc->srcu); } int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 919de6ffb821..f1085ccb7eed 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1570,11 +1570,12 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1593,7 +1594,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) msg->body.net_info.rssi = buffer[8]; msg->body.net_info.mgmt = kmemdup(&buffer[9], msg->body.net_info.frame_len, - GFP_ATOMIC); + GFP_KERNEL); if (!msg->body.net_info.mgmt) { kfree(msg); goto out; @@ -1606,7 +1607,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) @@ -1614,13 +1615,14 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) struct host_if_drv *hif_drv; struct host_if_msg *msg; struct wilc_vif *vif; + int srcu_idx; int result; int id; mutex_lock(&wilc->deinit_lock); id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1647,7 +1649,7 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) kfree(msg); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); mutex_unlock(&wilc->deinit_lock); } @@ -1655,11 +1657,12 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) { struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; int result; int id; id = get_unaligned_le32(&buffer[length - 4]); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) goto out; @@ -1684,7 +1687,7 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) } } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan, diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c index 73f56f7b002b..710e29bea560 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.c +++ b/drivers/net/wireless/microchip/wilc1000/netdev.c @@ -127,28 +127,30 @@ void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid, int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc) { + int srcu_idx; u8 ret_val = 0; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { if (!is_zero_ether_addr(vif->bssid)) ret_val++; } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return ret_val; } static void wilc_wake_tx_queues(struct wilc *wl) { + int srcu_idx; struct wilc_vif *ifc; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); wilc_for_each_vif(wl, ifc) { if (ifc->mac_opened && netif_queue_stopped(ifc->ndev)) netif_wake_queue(ifc->ndev); } - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); } static int wilc_txq_task(void *vp) @@ -653,6 +655,7 @@ static int wilc_set_mac_addr(struct net_device *dev, void *p) struct sockaddr *addr = (struct sockaddr *)p; unsigned char mac_addr[ETH_ALEN]; struct wilc_vif *tmp_vif; + int srcu_idx; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; @@ -664,19 +667,19 @@ static int wilc_set_mac_addr(struct net_device *dev, void *p) /* Verify MAC Address is not already in use: */ - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, tmp_vif) { wilc_get_mac_address(tmp_vif, mac_addr); if (ether_addr_equal(addr->sa_data, mac_addr)) { if (vif != tmp_vif) { - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return -EADDRNOTAVAIL; } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); return 0; } } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); result = wilc_set_mac_address(vif, (u8 *)addr->sa_data); if (result) @@ -764,14 +767,15 @@ netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) wilc_tx_complete); if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { + int srcu_idx; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { if (vif->mac_opened) netif_stop_queue(vif->ndev); } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } return NETDEV_TX_OK; @@ -815,12 +819,13 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, unsigned int frame_len = 0; struct wilc_vif *vif; struct sk_buff *skb; + int srcu_idx; int stats; if (!wilc) return; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_netdev = get_if_handler(wilc, buff); if (!wilc_netdev) goto out; @@ -848,14 +853,15 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); } out: - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) { + int srcu_idx; struct wilc_vif *vif; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff; u16 type = le16_to_cpup((__le16 *)buff); @@ -876,7 +882,7 @@ void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) if (vif->monitor_flag) wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); } - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); } static const struct net_device_ops wilc_netdev_ops = { @@ -906,7 +912,7 @@ void wilc_netdev_cleanup(struct wilc *wilc) list_del_rcu(&vif->list); wilc->vif_num--; mutex_unlock(&wilc->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wilc->srcu); if (vif->ndev) unregister_netdev(vif->ndev); } @@ -925,15 +931,16 @@ static u8 wilc_get_available_idx(struct wilc *wl) { int idx = 0; struct wilc_vif *vif; + int srcu_idx; - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wl->srcu); wilc_for_each_vif(wl, vif) { if (vif->idx == 0) idx = 1; else idx = 0; } - rcu_read_unlock(); + srcu_read_unlock(&wl->srcu, srcu_idx); return idx; } @@ -983,7 +990,7 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, list_add_tail_rcu(&vif->list, &wl->vif_list); wl->vif_num += 1; mutex_unlock(&wl->vif_mutex); - synchronize_rcu(); + synchronize_srcu(&wl->srcu); return vif; diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.h b/drivers/net/wireless/microchip/wilc1000/netdev.h index eecee3973d6a..fde8610a9c84 100644 --- a/drivers/net/wireless/microchip/wilc1000/netdev.h +++ b/drivers/net/wireless/microchip/wilc1000/netdev.h @@ -32,8 +32,8 @@ #define wilc_for_each_vif(w, v) \ struct wilc *_w = w; \ - list_for_each_entry_rcu(v, &_w->vif_list, list, \ - rcu_read_lock_held()) + list_for_each_entry_srcu(v, &_w->vif_list, list, \ + srcu_read_lock_held(&_w->srcu)) struct wilc_wfi_stats { unsigned long rx_packets; @@ -220,6 +220,14 @@ struct wilc { /* protect vif list */ struct mutex vif_mutex; + /* Sleepable RCU struct to manipulate vif list. Sleepable version is + * needed over the classic RCU version because the driver's current + * design involves some sleeping code while manipulating a vif + * retrieved from vif list (so in a SRCU critical section), like: + * - sending commands to the chip, using info from retrieved vif + * - registering a new monitoring net device + */ + struct srcu_struct srcu; u8 open_ifcs; /* protect head of transmit queue */ diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c index 37c32d17856e..a9e872a7b2c3 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.c +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c @@ -712,6 +712,7 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) u32 *vmm_table = wilc->vmm_table; u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0}; const struct wilc_hif_func *func; + int srcu_idx; u8 *txb = wilc->tx_buffer; struct wilc_vif *vif; @@ -723,10 +724,10 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) mutex_lock(&wilc->txq_add_to_head_cs); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&wilc->srcu); wilc_for_each_vif(wilc, vif) wilc_wlan_txq_filter_dup_tcp_ack(vif->ndev); - rcu_read_unlock(); + srcu_read_unlock(&wilc->srcu, srcu_idx); for (ac = 0; ac < NQUEUES; ac++) tqe_q[ac] = wilc_wlan_txq_get_first(wilc, ac); diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 2e60a6991ca1..42b7db12b1bd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -633,21 +633,6 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { - rtl_dbg(rtlpriv, COMP_MAC80211, DBG_LOUD, - "IEEE80211_CONF_CHANGE_RETRY_LIMITS %x\n", - hw->conf.long_frame_max_tx_count); - /* brought up everything changes (changed == ~0) indicates first - * open, so use our default value instead of that of wiphy. - */ - if (changed != ~0) { - mac->retry_long = hw->conf.long_frame_max_tx_count; - mac->retry_short = hw->conf.long_frame_max_tx_count; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, - (u8 *)(&hw->conf.long_frame_max_tx_count)); - } - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL && !rtlpriv->proximity.proxim_on) { struct ieee80211_channel *channel = hw->conf.chandef.chan; diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.c b/drivers/net/wwan/iosm/iosm_ipc_devlink.c index bef6819986e9..33d6342124bc 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_devlink.c +++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.c @@ -211,7 +211,7 @@ static int ipc_devlink_create_region(struct iosm_devlink *devlink) rc = PTR_ERR(devlink->cd_regions[i]); dev_err(devlink->dev, "Devlink region fail,err %d", rc); /* Delete previously created regions */ - for ( ; i >= 0; i--) + for (i--; i >= 0; i--) devlink_region_destroy(devlink->cd_regions[i]); goto region_create_fail; } diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index c6ad2148c2e0..ceb9c0ed3120 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -180,7 +180,7 @@ int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -226,7 +226,7 @@ int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) cmd.prop_get.offset = cpu_to_le32(off); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (ret >= 0) *val = le64_to_cpu(res.u64); @@ -271,7 +271,7 @@ int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) cmd.prop_set.value = cpu_to_le64(val); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, NULL, NULL, 0, - NVME_QID_ANY, 0); + NVME_QID_ANY, NVME_SUBMIT_RESERVED); if (unlikely(ret)) dev_err(ctrl->device, "Property Set error: %d, offset %#x\n", diff --git a/drivers/nvme/host/pr.c b/drivers/nvme/host/pr.c index e05571b2a1b0..8fa1ffcdaed4 100644 --- a/drivers/nvme/host/pr.c +++ b/drivers/nvme/host/pr.c @@ -77,7 +77,7 @@ static int nvme_sc_to_pr_err(int nvme_sc) if (nvme_is_path_error(nvme_sc)) return PR_STS_PATH_FAILED; - switch (nvme_sc) { + switch (nvme_sc & 0x7ff) { case NVME_SC_SUCCESS: return PR_STS_SUCCESS; case NVME_SC_RESERVATION_CONFLICT: diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 174900072c18..462375b293e4 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -25,6 +25,8 @@ #include <linux/string.h> #include <linux/slab.h> +#include "of_private.h" + /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @dev: Device node of the device whose interrupt is to be mapped @@ -96,6 +98,57 @@ static const char * const of_irq_imap_abusers[] = { NULL, }; +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_phandle_args *out_irq) +{ + u32 intsize, addrsize; + struct device_node *np; + + /* Get the interrupt parent */ + if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) + np = of_node_get(of_irq_dflt_pic); + else + np = of_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + + /* Check if not found */ + if (!np) { + pr_debug(" -> imap parent not found !\n"); + return NULL; + } + + /* Get #interrupt-cells and #address-cells of new parent */ + if (of_property_read_u32(np, "#interrupt-cells", + &intsize)) { + pr_debug(" -> parent lacks #interrupt-cells!\n"); + of_node_put(np); + return NULL; + } + if (of_property_read_u32(np, "#address-cells", + &addrsize)) + addrsize = 0; + + pr_debug(" -> intsize=%d, addrsize=%d\n", + intsize, addrsize); + + /* Check for malformed properties */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS) + || (len < (addrsize + intsize))) { + of_node_put(np); + return NULL; + } + + pr_debug(" -> imaplen=%d\n", len); + + imap += addrsize + intsize; + + out_irq->np = np; + for (int i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(imap - intsize + i); + out_irq->args_count = intsize; + + return imap; +} + /** * of_irq_parse_raw - Low level interrupt tree parsing * @addr: address specifier (start of "reg" property of the device) in be32 format @@ -112,12 +165,12 @@ static const char * const of_irq_imap_abusers[] = { */ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { - struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + struct device_node *ipar, *tnode, *old = NULL; __be32 initial_match_array[MAX_PHANDLE_ARGS]; const __be32 *match_array = initial_match_array; - const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i, rc = -EINVAL; + const __be32 *tmp, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; + u32 intsize = 1, addrsize; + int i, rc = -EINVAL; #ifdef DEBUG of_print_phandle_args("of_irq_parse_raw: ", out_irq); @@ -176,6 +229,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Now start the actual "proper" walk of the interrupt tree */ while (ipar != NULL) { + int imaplen, match; + const __be32 *imap, *oldimap, *imask; + struct device_node *newpar; /* * Now check if cursor is an interrupt-controller and * if it is then we are done, unless there is an @@ -216,7 +272,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Parse interrupt-map */ match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { + while (imaplen > (addrsize + intsize + 1)) { /* Compare specifiers */ match = 1; for (i = 0; i < (addrsize + intsize); i++, imaplen--) @@ -224,48 +280,17 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - else - newpar = of_find_node_by_phandle(be32_to_cpup(imap)); - imap++; - --imaplen; - - /* Check if not found */ - if (newpar == NULL) { - pr_debug(" -> imap parent not found !\n"); - goto fail; - } - - if (!of_device_is_available(newpar)) - match = 0; - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - if (of_property_read_u32(newpar, "#interrupt-cells", - &newintsize)) { - pr_debug(" -> parent lacks #interrupt-cells!\n"); - goto fail; - } - if (of_property_read_u32(newpar, "#address-cells", - &newaddrsize)) - newaddrsize = 0; - - pr_debug(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS) - || (imaplen < (newaddrsize + newintsize))) { - rc = -EFAULT; + oldimap = imap; + imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq); + if (!imap) goto fail; - } - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; + match &= of_device_is_available(out_irq->np); + if (match) + break; + of_node_put(out_irq->np); + imaplen -= imap - oldimap; pr_debug(" -> imaplen=%d\n", imaplen); } if (!match) { @@ -287,11 +312,11 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) * Successfully parsed an interrupt-map translation; copy new * interrupt specifier into the out_irq structure */ - match_array = imap - newaddrsize - newintsize; - for (i = 0; i < newintsize; i++) - out_irq->args[i] = be32_to_cpup(imap - newintsize + i); - out_irq->args_count = intsize = newintsize; - addrsize = newaddrsize; + match_array = oldimap + 1; + + newpar = out_irq->np; + intsize = out_irq->args_count; + addrsize = (imap - match_array) - intsize; if (ipar == newpar) { pr_debug("%pOF interrupt-map entry to self\n", ipar); @@ -300,7 +325,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) skiplevel: /* Iterate again with new parent */ - out_irq->np = newpar; pr_debug(" -> new parent: %pOF\n", newpar); of_node_put(ipar); ipar = newpar; @@ -310,7 +334,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) fail: of_node_put(ipar); - of_node_put(newpar); return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 94fc0aa07af9..04aa2a91f851 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -159,6 +159,9 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, extern int of_bus_n_addr_cells(struct device_node *np); extern int of_bus_n_size_cells(struct device_node *np); +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, + struct of_phandle_args *out_irq); + struct bus_dma_region; #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA) int of_dma_get_range(struct device_node *np, diff --git a/drivers/of/of_test.c b/drivers/of/of_test.c index a9301d293f01..c85a258bc6ae 100644 --- a/drivers/of/of_test.c +++ b/drivers/of/of_test.c @@ -54,4 +54,5 @@ static struct kunit_suite of_dtb_suite = { kunit_test_suites( &of_dtb_suite, ); +MODULE_DESCRIPTION("KUnit tests for OF APIs"); MODULE_LICENSE("GPL"); diff --git a/drivers/of/property.c b/drivers/of/property.c index 1c83e68f805b..164d77cb9445 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1306,10 +1306,10 @@ static struct device_node *parse_interrupts(struct device_node *np, static struct device_node *parse_interrupt_map(struct device_node *np, const char *prop_name, int index) { - const __be32 *imap, *imap_end, *addr; + const __be32 *imap, *imap_end; struct of_phandle_args sup_args; u32 addrcells, intcells; - int i, imaplen; + int imaplen; if (!IS_ENABLED(CONFIG_OF_IRQ)) return NULL; @@ -1322,33 +1322,23 @@ static struct device_node *parse_interrupt_map(struct device_node *np, addrcells = of_bus_n_addr_cells(np); imap = of_get_property(np, "interrupt-map", &imaplen); - if (!imap || imaplen <= (addrcells + intcells)) + imaplen /= sizeof(*imap); + if (!imap) return NULL; - imap_end = imap + imaplen; - while (imap < imap_end) { - addr = imap; - imap += addrcells; + imap_end = imap + imaplen; - sup_args.np = np; - sup_args.args_count = intcells; - for (i = 0; i < intcells; i++) - sup_args.args[i] = be32_to_cpu(imap[i]); - imap += intcells; + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; - /* - * Upon success, the function of_irq_parse_raw() returns - * interrupt controller DT node pointer in sup_args.np. - */ - if (of_irq_parse_raw(addr, &sup_args)) + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) return NULL; - if (!index) + if (i == index) return sup_args.np; of_node_put(sup_args.np); - imap += sup_args.args_count + 1; - index--; } return NULL; diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 30f031de9cfe..b123da16b63b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -289,8 +289,6 @@ void pci_cfg_access_lock(struct pci_dev *dev) { might_sleep(); - lock_map_acquire(&dev->cfg_access_lock); - raw_spin_lock_irq(&pci_lock); if (dev->block_cfg_access) pci_wait_cfg(dev); @@ -345,8 +343,6 @@ void pci_cfg_access_unlock(struct pci_dev *dev) raw_spin_unlock_irqrestore(&pci_lock, flags); wake_up_all(&pci_cfg_wait); - - lock_map_release(&dev->cfg_access_lock); } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 59e0949fb079..35fb1f17a589 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4883,7 +4883,6 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { - lock_map_assert_held(&dev->cfg_access_lock); pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8e696e547565..5fbabb4e3425 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2546,9 +2546,6 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; - lockdep_register_key(&dev->cfg_access_key); - lockdep_init_map(&dev->cfg_access_lock, dev_name(&dev->dev), - &dev->cfg_access_key, 0); dma_set_max_seg_size(&dev->dev, 65536); dma_set_seg_boundary(&dev->dev, 0xffffffff); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1953317541ea..665fa9524986 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -136,6 +136,7 @@ config YOGABOOK config YT2_1380 tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" depends on SERIAL_DEV_BUS + depends on EXTCON depends on ACPI help Say Y here to enable support for the custom fast charging protocol diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index d84ea66eecc6..8fcf38eed7f0 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -907,16 +907,44 @@ static int hsmp_plat_dev_register(void) return ret; } +/* + * This check is only needed for backward compatibility of previous platforms. + * All new platforms are expected to support ACPI based probing. + */ +static bool legacy_hsmp_support(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return false; + + switch (boot_cpu_data.x86) { + case 0x19: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + case 0x30 ... 0x3F: + case 0x90 ... 0x9F: + case 0xA0 ... 0xAF: + return true; + default: + return false; + } + case 0x1A: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + return true; + default: + return false; + } + default: + return false; + } + + return false; +} + static int __init hsmp_plt_init(void) { int ret = -ENODEV; - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { - pr_err("HSMP is not supported on Family:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - return ret; - } - /* * amd_nb_num() returns number of SMN/DF interfaces present in the system * if we have N SMN/DF interfaces that ideally means N sockets @@ -930,7 +958,15 @@ static int __init hsmp_plt_init(void) return ret; if (!plat_dev.is_acpi_device) { - ret = hsmp_plat_dev_register(); + if (legacy_hsmp_support()) { + /* Not ACPI device, but supports HSMP, register a plat_dev */ + ret = hsmp_plat_dev_register(); + } else { + /* Not ACPI, Does not support HSMP */ + pr_info("HSMP is not supported on Family:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + ret = -ENODEV; + } if (ret) platform_driver_unregister(&amd_hsmp_driver); } diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c index e61bfaf8b5c4..b562ed99ec4e 100644 --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/container_of.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/capability.h> @@ -25,11 +26,16 @@ static u32 da_supported_commands; static int da_num_tokens; static struct platform_device *platform_device; static struct calling_interface_token *da_tokens; -static struct device_attribute *token_location_attrs; -static struct device_attribute *token_value_attrs; +static struct token_sysfs_data *token_entries; static struct attribute **token_attrs; static DEFINE_MUTEX(smbios_mutex); +struct token_sysfs_data { + struct device_attribute location_attr; + struct device_attribute value_attr; + struct calling_interface_token *token; +}; + struct smbios_device { struct list_head list; struct device *device; @@ -416,47 +422,26 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) } } -static int match_attribute(struct device *dev, - struct device_attribute *attr) -{ - int i; - - for (i = 0; i < da_num_tokens * 2; i++) { - if (!token_attrs[i]) - continue; - if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) - return i/2; - } - dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); - return -EINVAL; -} - static ssize_t location_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i; + struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr); if (!capable(CAP_SYS_ADMIN)) return -EPERM; - i = match_attribute(dev, attr); - if (i > 0) - return sysfs_emit(buf, "%08x", da_tokens[i].location); - return 0; + return sysfs_emit(buf, "%08x", data->token->location); } static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i; + struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr); if (!capable(CAP_SYS_ADMIN)) return -EPERM; - i = match_attribute(dev, attr); - if (i > 0) - return sysfs_emit(buf, "%08x", da_tokens[i].value); - return 0; + return sysfs_emit(buf, "%08x", data->token->value); } static struct attribute_group smbios_attribute_group = { @@ -473,22 +458,15 @@ static int build_tokens_sysfs(struct platform_device *dev) { char *location_name; char *value_name; - size_t size; int ret; int i, j; - /* (number of tokens + 1 for null terminated */ - size = sizeof(struct device_attribute) * (da_num_tokens + 1); - token_location_attrs = kzalloc(size, GFP_KERNEL); - if (!token_location_attrs) + token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL); + if (!token_entries) return -ENOMEM; - token_value_attrs = kzalloc(size, GFP_KERNEL); - if (!token_value_attrs) - goto out_allocate_value; /* need to store both location and value + terminator*/ - size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); - token_attrs = kzalloc(size, GFP_KERNEL); + token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL); if (!token_attrs) goto out_allocate_attrs; @@ -496,32 +474,34 @@ static int build_tokens_sysfs(struct platform_device *dev) /* skip empty */ if (da_tokens[i].tokenID == 0) continue; + + token_entries[i].token = &da_tokens[i]; + /* add location */ location_name = kasprintf(GFP_KERNEL, "%04x_location", da_tokens[i].tokenID); if (location_name == NULL) goto out_unwind_strings; - sysfs_attr_init(&token_location_attrs[i].attr); - token_location_attrs[i].attr.name = location_name; - token_location_attrs[i].attr.mode = 0444; - token_location_attrs[i].show = location_show; - token_attrs[j++] = &token_location_attrs[i].attr; + + sysfs_attr_init(&token_entries[i].location_attr.attr); + token_entries[i].location_attr.attr.name = location_name; + token_entries[i].location_attr.attr.mode = 0444; + token_entries[i].location_attr.show = location_show; + token_attrs[j++] = &token_entries[i].location_attr.attr; /* add value */ value_name = kasprintf(GFP_KERNEL, "%04x_value", da_tokens[i].tokenID); - if (value_name == NULL) - goto loop_fail_create_value; - sysfs_attr_init(&token_value_attrs[i].attr); - token_value_attrs[i].attr.name = value_name; - token_value_attrs[i].attr.mode = 0444; - token_value_attrs[i].show = value_show; - token_attrs[j++] = &token_value_attrs[i].attr; - continue; - -loop_fail_create_value: - kfree(location_name); - goto out_unwind_strings; + if (!value_name) { + kfree(location_name); + goto out_unwind_strings; + } + + sysfs_attr_init(&token_entries[i].value_attr.attr); + token_entries[i].value_attr.attr.name = value_name; + token_entries[i].value_attr.attr.mode = 0444; + token_entries[i].value_attr.show = value_show; + token_attrs[j++] = &token_entries[i].value_attr.attr; } smbios_attribute_group.attrs = token_attrs; @@ -532,14 +512,12 @@ loop_fail_create_value: out_unwind_strings: while (i--) { - kfree(token_location_attrs[i].attr.name); - kfree(token_value_attrs[i].attr.name); + kfree(token_entries[i].location_attr.attr.name); + kfree(token_entries[i].value_attr.attr.name); } kfree(token_attrs); out_allocate_attrs: - kfree(token_value_attrs); -out_allocate_value: - kfree(token_location_attrs); + kfree(token_entries); return -ENOMEM; } @@ -551,12 +529,11 @@ static void free_group(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &smbios_attribute_group); for (i = 0; i < da_num_tokens; i++) { - kfree(token_location_attrs[i].attr.name); - kfree(token_value_attrs[i].attr.name); + kfree(token_entries[i].location_attr.attr.name); + kfree(token_entries[i].value_attr.attr.name); } kfree(token_attrs); - kfree(token_value_attrs); - kfree(token_location_attrs); + kfree(token_entries); } static int __init dell_smbios_init(void) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 2d9ca2292ea1..f74af0a689f2 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -34,7 +34,6 @@ static const struct property_entry archos_101_cesium_educ_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-archos-101-cesium-educ.fw"), { } @@ -49,7 +48,6 @@ static const struct property_entry bush_bush_windows_tablet_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1850), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-bush-bush-windows-tablet.fw"), { } @@ -79,7 +77,6 @@ static const struct property_entry chuwi_hi8_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-hi8-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -95,7 +92,6 @@ static const struct property_entry chuwi_hi8_pro_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -123,7 +119,6 @@ static const struct property_entry chuwi_hi10_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 5), PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 4), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -139,7 +134,6 @@ static const struct property_entry chuwi_hi10_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1908), PROPERTY_ENTRY_U32("touchscreen-size-y", 1270), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("silead,pen-supported"), PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8), @@ -171,7 +165,6 @@ static const struct property_entry chuwi_hi10_pro_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-pro.fw"), PROPERTY_ENTRY_U32_ARRAY("silead,efi-fw-min-max", chuwi_hi10_pro_efi_min_max), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("silead,pen-supported"), PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8), @@ -201,7 +194,6 @@ static const struct property_entry chuwi_hibook_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hibook.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -227,7 +219,6 @@ static const struct property_entry chuwi_vi8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-vi8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -255,7 +246,6 @@ static const struct property_entry chuwi_vi10_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1858), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-vi10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -271,7 +261,6 @@ static const struct property_entry chuwi_surbook_mini_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2040), PROPERTY_ENTRY_U32("touchscreen-size-y", 1524), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-surbook-mini.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), { } }; @@ -289,7 +278,6 @@ static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-connect-tablet9.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -306,7 +294,6 @@ static const struct property_entry csl_panther_tab_hd_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-csl-panther-tab-hd.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -322,7 +309,6 @@ static const struct property_entry cube_iwork8_air_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -346,7 +332,6 @@ static const struct property_entry cube_knote_i1101_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1961), PROPERTY_ENTRY_U32("touchscreen-size-y", 1513), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-cube-knote-i1101.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -360,7 +345,6 @@ static const struct property_entry dexp_ursus_7w_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 890), PROPERTY_ENTRY_U32("touchscreen-size-y", 630), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -376,7 +360,6 @@ static const struct property_entry dexp_ursus_kx210i_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1720), PROPERTY_ENTRY_U32("touchscreen-size-y", 1137), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-dexp-ursus-kx210i.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -391,7 +374,6 @@ static const struct property_entry digma_citi_e200_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-digma_citi_e200.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -450,7 +432,6 @@ static const struct property_entry irbis_tw90_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-irbis_tw90.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -466,7 +447,6 @@ static const struct property_entry irbis_tw118_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1960), PROPERTY_ENTRY_U32("touchscreen-size-y", 1510), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-irbis-tw118.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -483,7 +463,6 @@ static const struct property_entry itworks_tw891_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -496,7 +475,6 @@ static const struct property_entry jumper_ezpad_6_pro_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -511,7 +489,6 @@ static const struct property_entry jumper_ezpad_6_pro_b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro-b.fw"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -527,7 +504,6 @@ static const struct property_entry jumper_ezpad_6_m4_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1950), PROPERTY_ENTRY_U32("touchscreen-size-y", 1525), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-m4.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -544,7 +520,6 @@ static const struct property_entry jumper_ezpad_7_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1526), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-jumper-ezpad-7.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,stuck-controller-bug"), { } }; @@ -561,7 +536,6 @@ static const struct property_entry jumper_ezpad_mini3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -578,7 +552,6 @@ static const struct property_entry mpman_converter9_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-mpman-converter9.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -594,7 +567,6 @@ static const struct property_entry mpman_mpwin895cl_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -611,7 +583,6 @@ static const struct property_entry myria_my8307_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-myria-my8307.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -628,7 +599,6 @@ static const struct property_entry onda_obook_20_plus_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-obook-20-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -645,7 +615,6 @@ static const struct property_entry onda_v80_plus_v3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v80-plus-v3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -669,7 +638,6 @@ static const struct property_entry onda_v820w_32g_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-onda-v820w-32g.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -687,7 +655,6 @@ static const struct property_entry onda_v891_v5_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v891-v5.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -703,7 +670,6 @@ static const struct property_entry onda_v891w_v1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1676), PROPERTY_ENTRY_U32("touchscreen-size-y", 1130), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-onda-v891w-v1.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -720,7 +686,6 @@ static const struct property_entry onda_v891w_v3_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1135), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v891w-v3.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -759,7 +724,6 @@ static const struct property_entry pipo_w11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1984), PROPERTY_ENTRY_U32("touchscreen-size-y", 1532), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-pipo-w11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -775,7 +739,6 @@ static const struct property_entry positivo_c4128b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1915), PROPERTY_ENTRY_U32("touchscreen-size-y", 1269), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-positivo-c4128b.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -791,7 +754,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v20_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1146), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-pov-mobii-wintab-p800w-v20.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -808,7 +770,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -825,7 +786,6 @@ static const struct property_entry pov_mobii_wintab_p1006w_v10_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1520), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p1006w-v10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -842,7 +802,6 @@ static const struct property_entry predia_basic_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1144), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-predia-basic.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -859,7 +818,6 @@ static const struct property_entry rca_cambio_w101_v2_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 874), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rca-cambio-w101-v2.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -874,7 +832,6 @@ static const struct property_entry rwc_nanote_p8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-p8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -890,7 +847,6 @@ static const struct property_entry schneider_sct101ctm_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-schneider-sct101ctm.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -906,7 +862,6 @@ static const struct property_entry globalspace_solt_ivw116_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1723), PROPERTY_ENTRY_U32("touchscreen-size-y", 1077), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-globalspace-solt-ivw116.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -923,7 +878,6 @@ static const struct property_entry techbite_arc_11_6_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1270), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -939,7 +893,6 @@ static const struct property_entry teclast_tbook11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1264), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-tbook11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -965,7 +918,6 @@ static const struct property_entry teclast_x16_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1264), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-x16-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -988,7 +940,6 @@ static const struct property_entry teclast_x3_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-teclast-x3-plus.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1004,7 +955,6 @@ static const struct property_entry teclast_x98plus2_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-teclast_x98plus2.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -1018,7 +968,6 @@ static const struct property_entry trekstor_primebook_c11_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1530), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c11.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1032,7 +981,6 @@ static const struct property_entry trekstor_primebook_c13_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2624), PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c13.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1046,7 +994,6 @@ static const struct property_entry trekstor_primetab_t13b_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2500), PROPERTY_ENTRY_U32("touchscreen-size-y", 1900), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primetab-t13b.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), { } @@ -1074,7 +1021,6 @@ static const struct property_entry trekstor_surftab_twin_10_1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1090,7 +1036,6 @@ static const struct property_entry trekstor_surftab_wintron70_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 884), PROPERTY_ENTRY_U32("touchscreen-size-y", 632), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-surftab-wintron70-st70416-6.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1107,7 +1052,6 @@ static const struct property_entry viglen_connect_10_props[] = { PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 6), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-viglen-connect-10.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1121,7 +1065,6 @@ static const struct property_entry vinga_twizzle_j116_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1920), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"), - PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -1907,7 +1850,7 @@ static int __init ts_parse_props(char *str) u32 u32val; int i, ret; - strscpy(orig_str, str, sizeof(orig_str)); + strscpy(orig_str, str); /* * str is part of the static_command_line from init/main.c and poking diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index e74a0f6a3157..4e80273dfb1e 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -6,6 +6,7 @@ extern struct mutex pnp_lock; extern const struct attribute_group *pnp_dev_groups[]; +extern const struct bus_type pnp_bus_type; int pnp_register_protocol(struct pnp_protocol *protocol); void pnp_unregister_protocol(struct pnp_protocol *protocol); diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 0a5d0d8befa8..3483e52e3a81 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -266,6 +266,12 @@ const struct bus_type pnp_bus_type = { .dev_groups = pnp_dev_groups, }; +bool dev_is_pnp(const struct device *dev) +{ + return dev->bus == &pnp_bus_type; +} +EXPORT_SYMBOL_GPL(dev_is_pnp); + int pnp_register_driver(struct pnp_driver *drv) { drv->driver.name = drv->name; diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 7513018c9f9a..2067b0120d08 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -85,7 +85,8 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, } if (info->verify(info, pin, func, chan)) { - pr_err("driver cannot use function %u on pin %u\n", func, chan); + pr_err("driver cannot use function %u and channel %u on pin %u\n", + func, chan, pin); return -EOPNOTSUPP; } diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index a226dc1b65d7..4eb0837298d4 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -414,28 +414,40 @@ static char print_alua_state(unsigned char state) } } -static enum scsi_disposition alua_check_sense(struct scsi_device *sdev, - struct scsi_sense_hdr *sense_hdr) +static void alua_handle_state_transition(struct scsi_device *sdev) { struct alua_dh_data *h = sdev->handler_data; struct alua_port_group *pg; + rcu_read_lock(); + pg = rcu_dereference(h->pg); + if (pg) + pg->state = SCSI_ACCESS_STATE_TRANSITIONING; + rcu_read_unlock(); + alua_check(sdev, false); +} + +static enum scsi_disposition alua_check_sense(struct scsi_device *sdev, + struct scsi_sense_hdr *sense_hdr) +{ switch (sense_hdr->sense_key) { case NOT_READY: if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { /* * LUN Not Accessible - ALUA state transition */ - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (pg) - pg->state = SCSI_ACCESS_STATE_TRANSITIONING; - rcu_read_unlock(); - alua_check(sdev, false); + alua_handle_state_transition(sdev); return NEEDS_RETRY; } break; case UNIT_ATTENTION: + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { + /* + * LUN Not Accessible - ALUA state transition + */ + alua_handle_state_transition(sdev); + return NEEDS_RETRY; + } if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) { /* * Power On, Reset, or Bus Device Reset. @@ -502,7 +514,8 @@ static int alua_tur(struct scsi_device *sdev) retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ, ALUA_FAILOVER_RETRIES, &sense_hdr); - if (sense_hdr.sense_key == NOT_READY && + if ((sense_hdr.sense_key == NOT_READY || + sense_hdr.sense_key == UNIT_ATTENTION) && sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) return SCSI_DH_RETRY; else if (retval) diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c index 329cc6ec3b58..82aa4e418c5a 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_transport.c +++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c @@ -1364,7 +1364,7 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, continue; if (i > sizeof(mr_sas_port->phy_mask) * 8) { - ioc_warn(mrioc, "skipping port %u, max allowed value is %lu\n", + ioc_warn(mrioc, "skipping port %u, max allowed value is %zu\n", i, sizeof(mr_sas_port->phy_mask) * 8); goto out_fail; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 89ef43a5ef86..12d08d8ba538 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -302,8 +302,8 @@ struct _scsi_io_transfer { /** * _scsih_set_debug_level - global setting of ioc->logging_level. - * @val: ? - * @kp: ? + * @val: value of the parameter to be set + * @kp: pointer to kernel_param structure * * Note: The logging levels are defined in mpt3sas_debug.h. */ diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 5058e01b65a2..98afdfe63600 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -363,6 +363,7 @@ struct qedf_ctx { #define QEDF_IN_RECOVERY 5 #define QEDF_DBG_STOP_IO 6 #define QEDF_PROBING 8 +#define QEDF_STAG_IN_PROGRESS 9 unsigned long flags; /* Miscellaneous state flags */ int fipvlan_retries; u8 num_queues; diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index fd12439cbaab..49adddf978cc 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -318,11 +318,18 @@ static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did, */ if (resp == fc_lport_flogi_resp) { qedf->flogi_cnt++; + qedf->flogi_pending++; + + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n"); + qedf->flogi_pending = 0; + } + if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) { schedule_delayed_work(&qedf->stag_work, 2); return NULL; } - qedf->flogi_pending++; + return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp, arg, timeout); } @@ -912,13 +919,14 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) struct qedf_ctx *qedf; struct qed_link_output if_link; + qedf = lport_priv(lport); + if (lport->vport) { + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); printk_ratelimited("Cannot issue host reset on NPIV port.\n"); return; } - qedf = lport_priv(lport); - qedf->flogi_pending = 0; /* For host reset, essentially do a soft link up/down */ atomic_set(&qedf->link_state, QEDF_LINK_DOWN); @@ -938,6 +946,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) if (!if_link.link_up) { QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Physical link is not up.\n"); + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); return; } /* Flush and wait to make sure link down is processed */ @@ -950,6 +959,7 @@ void qedf_ctx_soft_reset(struct fc_lport *lport) "Queue link up work.\n"); queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 0); + clear_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); } /* Reset the host by gracefully logging out and then logging back in */ @@ -3463,6 +3473,7 @@ retry_probe: } /* Start the Slowpath-process */ + memset(&slowpath_params, 0, sizeof(struct qed_slowpath_params)); slowpath_params.int_mode = QED_INT_MODE_MSIX; slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER; slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER; @@ -3721,6 +3732,7 @@ static void __qedf_remove(struct pci_dev *pdev, int mode) { struct qedf_ctx *qedf; int rc; + int cnt = 0; if (!pdev) { QEDF_ERR(NULL, "pdev is NULL.\n"); @@ -3738,6 +3750,17 @@ static void __qedf_remove(struct pci_dev *pdev, int mode) return; } +stag_in_prog: + if (test_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Stag in progress, cnt=%d.\n", cnt); + cnt++; + + if (cnt < 5) { + msleep(500); + goto stag_in_prog; + } + } + if (mode != QEDF_MODE_RECOVERY) set_bit(QEDF_UNLOADING, &qedf->flags); @@ -3997,6 +4020,24 @@ void qedf_stag_change_work(struct work_struct *work) struct qedf_ctx *qedf = container_of(work, struct qedf_ctx, stag_work.work); + if (!qedf) { + QEDF_ERR(&qedf->dbg_ctx, "qedf is NULL"); + return; + } + + if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, + "Already is in recovery, hence not calling software context reset.\n"); + return; + } + + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Driver unloading\n"); + return; + } + + set_bit(QEDF_STAG_IN_PROGRESS, &qedf->flags); + printk_ratelimited("[%s]:[%s:%d]:%d: Performing software context reset.", dev_name(&qedf->pdev->dev), __func__, __LINE__, qedf->dbg_ctx.host_no); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3e0c0381277a..f0464db3f9de 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -350,6 +350,13 @@ static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page) if (result < SCSI_VPD_HEADER_SIZE) return 0; + if (result > sizeof(vpd)) { + dev_warn_once(&sdev->sdev_gendev, + "%s: long VPD page 0 length: %d bytes\n", + __func__, result); + result = sizeof(vpd); + } + result -= SCSI_VPD_HEADER_SIZE; if (!memchr(&vpd[SCSI_VPD_HEADER_SIZE], page, result)) return 0; diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index 1175f2e213b5..dc899277b3a4 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -65,7 +65,7 @@ int sr_disk_status(struct cdrom_device_info *); int sr_get_last_session(struct cdrom_device_info *, struct cdrom_multisession *); int sr_get_mcn(struct cdrom_device_info *, struct cdrom_mcn *); int sr_reset(struct cdrom_device_info *); -int sr_select_speed(struct cdrom_device_info *cdi, int speed); +int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed); int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *); int sr_is_xa(Scsi_CD *); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 5b0b35e60e61..a0d2556a27bb 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -425,11 +425,14 @@ int sr_reset(struct cdrom_device_info *cdi) return 0; } -int sr_select_speed(struct cdrom_device_info *cdi, int speed) +int sr_select_speed(struct cdrom_device_info *cdi, unsigned long speed) { Scsi_CD *cd = cdi->handle; struct packet_command cgc; + /* avoid exceeding the max speed or overflowing integer bounds */ + speed = clamp(0, speed, 0xffff / 177); + if (speed == 0) speed = 0xffff; /* set to max */ else diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index f498db9abe35..76bb496305a0 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -1777,13 +1777,28 @@ static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsign return qmc_chan; } -struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) +int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name) +{ + int count; + + /* phandles are fixed args phandles with one arg */ + count = of_count_phandle_with_args(np, phandles_name, NULL); + if (count < 0) + return count; + + return count / 2; +} +EXPORT_SYMBOL(qmc_chan_count_phandles); + +struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, + const char *phandles_name, + int index) { struct of_phandle_args out_args; struct qmc_chan *qmc_chan; int ret; - ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, + ret = of_parse_phandle_with_fixed_args(np, phandles_name, 1, index, &out_args); if (ret < 0) return ERR_PTR(ret); @@ -1797,7 +1812,7 @@ struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phan of_node_put(out_args.np); return qmc_chan; } -EXPORT_SYMBOL(qmc_chan_get_byphandle); +EXPORT_SYMBOL(qmc_chan_get_byphandles_index); struct qmc_chan *qmc_chan_get_bychild(struct device_node *np) { @@ -1827,9 +1842,10 @@ static void devm_qmc_chan_release(struct device *dev, void *res) qmc_chan_put(*qmc_chan); } -struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, - struct device_node *np, - const char *phandle_name) +struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev, + struct device_node *np, + const char *phandles_name, + int index) { struct qmc_chan *qmc_chan; struct qmc_chan **dr; @@ -1838,7 +1854,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - qmc_chan = qmc_chan_get_byphandle(np, phandle_name); + qmc_chan = qmc_chan_get_byphandles_index(np, phandles_name, index); if (!IS_ERR(qmc_chan)) { *dr = qmc_chan; devres_add(dev, dr); @@ -1848,7 +1864,7 @@ struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, return qmc_chan; } -EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); +EXPORT_SYMBOL(devm_qmc_chan_get_byphandles_index); struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 9963b92eb505..f1a4df6cfebd 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -97,18 +97,13 @@ static bool find_slave(struct sdw_bus *bus, struct acpi_device *adev, struct sdw_slave_id *id) { - u64 addr; unsigned int link_id; - acpi_status status; - - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); + u64 addr; + int ret; - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); + ret = acpi_get_local_u64_address(adev->handle, &addr); + if (ret < 0) return false; - } if (bus->ops->override_adr) addr = bus->ops->override_adr(bus, addr); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 54cce4e523bc..30567b499455 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -467,6 +467,21 @@ static void thermal_governor_trip_crossed(struct thermal_governor *governor, governor->trip_crossed(tz, trip, crossed_up); } +static void thermal_trip_crossed(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_governor *governor, + bool crossed_up) +{ + if (crossed_up) { + thermal_notify_tz_trip_up(tz, trip); + thermal_debug_tz_trip_up(tz, trip); + } else { + thermal_notify_tz_trip_down(tz, trip); + thermal_debug_tz_trip_down(tz, trip); + } + thermal_governor_trip_crossed(governor, tz, trip, crossed_up); +} + static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, const struct list_head *b) { @@ -506,18 +521,12 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, handle_thermal_trip(tz, td, &way_up_list, &way_down_list); list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp); - list_for_each_entry(td, &way_up_list, notify_list_node) { - thermal_notify_tz_trip_up(tz, &td->trip); - thermal_debug_tz_trip_up(tz, &td->trip); - thermal_governor_trip_crossed(governor, tz, &td->trip, true); - } + list_for_each_entry(td, &way_up_list, notify_list_node) + thermal_trip_crossed(tz, &td->trip, governor, true); list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); - list_for_each_entry(td, &way_down_list, notify_list_node) { - thermal_notify_tz_trip_down(tz, &td->trip); - thermal_debug_tz_trip_down(tz, &td->trip); - thermal_governor_trip_crossed(governor, tz, &td->trip, false); - } + list_for_each_entry(td, &way_down_list, notify_list_node) + thermal_trip_crossed(tz, &td->trip, governor, false); if (governor->manage) governor->manage(tz); @@ -593,6 +602,12 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_zone_device_update); +void thermal_zone_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip) +{ + thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); +} + int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), void *data) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index d9785e5bbb08..20e7b45673d6 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -246,6 +246,8 @@ int thermal_zone_trip_id(const struct thermal_zone_device *tz, void thermal_zone_trip_updated(struct thermal_zone_device *tz, const struct thermal_trip *trip); int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); +void thermal_zone_trip_down(struct thermal_zone_device *tz, + const struct thermal_trip *trip); /* sysfs I/F */ int thermal_zone_create_device_groups(struct thermal_zone_device *tz); diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index 91f9c21235a8..942447229157 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -91,6 +91,8 @@ struct cdev_record { * * @timestamp: the trip crossing timestamp * @duration: total time when the zone temperature was above the trip point + * @trip_temp: trip temperature at mitigation start + * @trip_hyst: trip hysteresis at mitigation start * @count: the number of times the zone temperature was above the trip point * @max: maximum recorded temperature above the trip point * @min: minimum recorded temperature above the trip point @@ -99,6 +101,8 @@ struct cdev_record { struct trip_stats { ktime_t timestamp; ktime_t duration; + int trip_temp; + int trip_hyst; int count; int max; int min; @@ -574,6 +578,7 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, struct thermal_debugfs *thermal_dbg = tz->debugfs; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); + struct trip_stats *trip_stats; if (!thermal_dbg) return; @@ -639,7 +644,10 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id; tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - tze->trip_stats[trip_id].timestamp = now; + trip_stats = &tze->trip_stats[trip_id]; + trip_stats->trip_temp = trip->temperature; + trip_stats->trip_hyst = trip->hysteresis; + trip_stats->timestamp = now; unlock: mutex_unlock(&thermal_dbg->lock); @@ -794,10 +802,6 @@ static int tze_seq_show(struct seq_file *s, void *v) const struct thermal_trip *trip = &td->trip; struct trip_stats *trip_stats; - /* Skip invalid trips. */ - if (trip->temperature == THERMAL_TEMP_INVALID) - continue; - /* * There is no possible mitigation happening at the * critical trip point, so the stats will be always @@ -836,8 +840,8 @@ static int tze_seq_show(struct seq_file *s, void *v) seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, - 9, trip->temperature, - 9, trip->hysteresis, + 9, trip_stats->trip_temp, + 9, trip_stats->trip_hyst, c, 10, duration_ms, 9, trip_stats->avg, 9, trip_stats->min, diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index d6a6acc78ddb..49e63db68517 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -152,17 +152,23 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, if (trip->temperature == temp) return; + trip->temperature = temp; + thermal_notify_tz_trip_change(tz, trip); + if (temp == THERMAL_TEMP_INVALID) { struct thermal_trip_desc *td = trip_to_trip_desc(trip); - if (trip->type == THERMAL_TRIP_PASSIVE && - tz->temperature >= td->threshold) { + if (tz->temperature >= td->threshold) { /* - * The trip has been crossed, so the thermal zone's - * passive count needs to be adjusted. + * The trip has been crossed on the way up, so some + * adjustments are needed to compensate for the lack + * of it going forward. */ - tz->passive--; - WARN_ON_ONCE(tz->passive < 0); + if (trip->type == THERMAL_TRIP_PASSIVE) { + tz->passive--; + WARN_ON_ONCE(tz->passive < 0); + } + thermal_zone_trip_down(tz, trip); } /* * Invalidate the threshold to avoid triggering a spurious @@ -170,7 +176,5 @@ void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, */ td->threshold = INT_MAX; } - trip->temperature = temp; - thermal_notify_tz_trip_change(tz, trip); } EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 005d63ab1f44..8944548c30fa 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -634,20 +634,20 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) struct ufshcd_lrb *lrbp = &hba->lrb[tag]; struct ufs_hw_queue *hwq; unsigned long flags; - int err = FAILED; + int err; if (!ufshcd_cmd_inflight(lrbp->cmd)) { dev_err(hba->dev, "%s: skip abort. cmd at tag %d already completed.\n", __func__, tag); - goto out; + return FAILED; } /* Skip task abort in case previous aborts failed and report failure */ if (lrbp->req_abort_skip) { dev_err(hba->dev, "%s: skip abort. tag %d failed earlier\n", __func__, tag); - goto out; + return FAILED; } hwq = ufshcd_mcq_req_to_hwq(hba, scsi_cmd_to_rq(cmd)); @@ -659,7 +659,7 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) */ dev_err(hba->dev, "%s: cmd found in sq. hwq=%d, tag=%d\n", __func__, hwq->id, tag); - goto out; + return FAILED; } /* @@ -667,18 +667,17 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) * in the completion queue either. Query the device to see if * the command is being processed in the device. */ - if (ufshcd_try_to_abort_task(hba, tag)) { + err = ufshcd_try_to_abort_task(hba, tag); + if (err) { dev_err(hba->dev, "%s: device abort failed %d\n", __func__, err); lrbp->req_abort_skip = true; - goto out; + return FAILED; } - err = SUCCESS; spin_lock_irqsave(&hwq->cq_lock, flags); if (ufshcd_cmd_inflight(lrbp->cmd)) ufshcd_release_scsi_cmd(hba, lrbp); spin_unlock_irqrestore(&hwq->cq_lock, flags); -out: - return err; + return SUCCESS; } diff --git a/fs/bcachefs/btree_locking.c b/fs/bcachefs/btree_locking.c index c3e9b0cc7bbd..d66fff22109a 100644 --- a/fs/bcachefs/btree_locking.c +++ b/fs/bcachefs/btree_locking.c @@ -215,6 +215,7 @@ static noinline int break_cycle(struct lock_graph *g, struct printbuf *cycle) if (unlikely(!best)) { struct printbuf buf = PRINTBUF; + buf.atomic++; prt_printf(&buf, bch2_fmt(g->g->trans->c, "cycle of nofail locks")); diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c index 8171f947fac8..6e477fadaa2a 100644 --- a/fs/bcachefs/move.c +++ b/fs/bcachefs/move.c @@ -547,6 +547,7 @@ static int bch2_move_data_btree(struct moving_context *ctxt, ctxt->stats->pos = BBPOS(btree_id, start); } + bch2_trans_begin(trans); bch2_trans_iter_init(trans, &iter, btree_id, start, BTREE_ITER_prefetch| BTREE_ITER_all_snapshots); @@ -920,7 +921,20 @@ static bool rereplicate_pred(struct bch_fs *c, void *arg, ? c->opts.metadata_replicas : io_opts->data_replicas; - if (!nr_good || nr_good >= replicas) + rcu_read_lock(); + struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); + unsigned i = 0; + bkey_for_each_ptr(ptrs, ptr) { + struct bch_dev *ca = bch2_dev_rcu(c, ptr->dev); + if (!ptr->cached && + (!ca || !ca->mi.durability)) + data_opts->kill_ptrs |= BIT(i); + i++; + } + rcu_read_unlock(); + + if (!data_opts->kill_ptrs && + (!nr_good || nr_good >= replicas)) return false; data_opts->target = 0; diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 91c994b569f3..6ed495ca7a31 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -89,6 +89,16 @@ enum { BTRFS_INODE_FREE_SPACE_INODE, /* Set when there are no capabilities in XATTs for the inode. */ BTRFS_INODE_NO_CAP_XATTR, + /* + * Set if an error happened when doing a COW write before submitting a + * bio or during writeback. Used for both buffered writes and direct IO + * writes. This is to signal a fast fsync that it has to wait for + * ordered extents to complete and therefore not log extent maps that + * point to unwritten extents (when an ordered extent completes and it + * has the BTRFS_ORDERED_IOERR flag set, it drops extent maps in its + * range). + */ + BTRFS_INODE_COW_WRITE_ERROR, }; /* in memory btrfs inode */ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 1b20b3e390df..38cdb8875e8e 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4538,18 +4538,10 @@ static void btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, struct btrfs_fs_info *fs_info) { struct rb_node *node; - struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_delayed_ref_root *delayed_refs = &trans->delayed_refs; struct btrfs_delayed_ref_node *ref; - delayed_refs = &trans->delayed_refs; - spin_lock(&delayed_refs->lock); - if (atomic_read(&delayed_refs->num_entries) == 0) { - spin_unlock(&delayed_refs->lock); - btrfs_debug(fs_info, "delayed_refs has NO entry"); - return; - } - while ((node = rb_first_cached(&delayed_refs->href_root)) != NULL) { struct btrfs_delayed_ref_head *head; struct rb_node *n; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 597387e9f040..f688fab55251 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3689,6 +3689,8 @@ static struct extent_buffer *grab_extent_buffer( struct folio *folio = page_folio(page); struct extent_buffer *exists; + lockdep_assert_held(&page->mapping->i_private_lock); + /* * For subpage case, we completely rely on radix tree to ensure we * don't try to insert two ebs for the same bytenr. So here we always @@ -3756,13 +3758,14 @@ static int check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) * The caller needs to free the existing folios and retry using the same order. */ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, + struct btrfs_subpage *prealloc, struct extent_buffer **found_eb_ret) { struct btrfs_fs_info *fs_info = eb->fs_info; struct address_space *mapping = fs_info->btree_inode->i_mapping; const unsigned long index = eb->start >> PAGE_SHIFT; - struct folio *existing_folio; + struct folio *existing_folio = NULL; int ret; ASSERT(found_eb_ret); @@ -3774,12 +3777,14 @@ retry: ret = filemap_add_folio(mapping, eb->folios[i], index + i, GFP_NOFS | __GFP_NOFAIL); if (!ret) - return 0; + goto finish; existing_folio = filemap_lock_folio(mapping, index + i); /* The page cache only exists for a very short time, just retry. */ - if (IS_ERR(existing_folio)) + if (IS_ERR(existing_folio)) { + existing_folio = NULL; goto retry; + } /* For now, we should only have single-page folios for btree inode. */ ASSERT(folio_nr_pages(existing_folio) == 1); @@ -3790,14 +3795,13 @@ retry: return -EAGAIN; } - if (fs_info->nodesize < PAGE_SIZE) { - /* - * We're going to reuse the existing page, can drop our page - * and subpage structure now. - */ +finish: + spin_lock(&mapping->i_private_lock); + if (existing_folio && fs_info->nodesize < PAGE_SIZE) { + /* We're going to reuse the existing page, can drop our folio now. */ __free_page(folio_page(eb->folios[i], 0)); eb->folios[i] = existing_folio; - } else { + } else if (existing_folio) { struct extent_buffer *existing_eb; existing_eb = grab_extent_buffer(fs_info, @@ -3805,6 +3809,7 @@ retry: if (existing_eb) { /* The extent buffer still exists, we can use it directly. */ *found_eb_ret = existing_eb; + spin_unlock(&mapping->i_private_lock); folio_unlock(existing_folio); folio_put(existing_folio); return 1; @@ -3813,6 +3818,22 @@ retry: __free_page(folio_page(eb->folios[i], 0)); eb->folios[i] = existing_folio; } + eb->folio_size = folio_size(eb->folios[i]); + eb->folio_shift = folio_shift(eb->folios[i]); + /* Should not fail, as we have preallocated the memory. */ + ret = attach_extent_buffer_folio(eb, eb->folios[i], prealloc); + ASSERT(!ret); + /* + * To inform we have an extra eb under allocation, so that + * detach_extent_buffer_page() won't release the folio private when the + * eb hasn't been inserted into radix tree yet. + * + * The ref will be decreased when the eb releases the page, in + * detach_extent_buffer_page(). Thus needs no special handling in the + * error path. + */ + btrfs_folio_inc_eb_refs(fs_info, eb->folios[i]); + spin_unlock(&mapping->i_private_lock); return 0; } @@ -3824,7 +3845,6 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, int attached = 0; struct extent_buffer *eb; struct extent_buffer *existing_eb = NULL; - struct address_space *mapping = fs_info->btree_inode->i_mapping; struct btrfs_subpage *prealloc = NULL; u64 lockdep_owner = owner_root; bool page_contig = true; @@ -3890,7 +3910,7 @@ reallocate: for (int i = 0; i < num_folios; i++) { struct folio *folio; - ret = attach_eb_folio_to_filemap(eb, i, &existing_eb); + ret = attach_eb_folio_to_filemap(eb, i, prealloc, &existing_eb); if (ret > 0) { ASSERT(existing_eb); goto out; @@ -3927,24 +3947,6 @@ reallocate: * and free the allocated page. */ folio = eb->folios[i]; - eb->folio_size = folio_size(folio); - eb->folio_shift = folio_shift(folio); - spin_lock(&mapping->i_private_lock); - /* Should not fail, as we have preallocated the memory */ - ret = attach_extent_buffer_folio(eb, folio, prealloc); - ASSERT(!ret); - /* - * To inform we have extra eb under allocation, so that - * detach_extent_buffer_page() won't release the folio private - * when the eb hasn't yet been inserted into radix tree. - * - * The ref will be decreased when the eb released the page, in - * detach_extent_buffer_page(). - * Thus needs no special handling in error path. - */ - btrfs_folio_inc_eb_refs(fs_info, folio); - spin_unlock(&mapping->i_private_lock); - WARN_ON(btrfs_folio_test_dirty(fs_info, folio, eb->start, eb->len)); /* diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index e764ac3f22e2..d90138683a0a 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1885,6 +1885,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) */ if (full_sync || btrfs_is_zoned(fs_info)) { ret = btrfs_wait_ordered_range(inode, start, len); + clear_bit(BTRFS_INODE_COW_WRITE_ERROR, &BTRFS_I(inode)->runtime_flags); } else { /* * Get our ordered extents as soon as possible to avoid doing @@ -1894,6 +1895,21 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) btrfs_get_ordered_extents_for_logging(BTRFS_I(inode), &ctx.ordered_extents); ret = filemap_fdatawait_range(inode->i_mapping, start, end); + if (ret) + goto out_release_extents; + + /* + * Check and clear the BTRFS_INODE_COW_WRITE_ERROR now after + * starting and waiting for writeback, because for buffered IO + * it may have been set during the end IO callback + * (end_bbio_data_write() -> btrfs_finish_ordered_extent()) in + * case an error happened and we need to wait for ordered + * extents to complete so that any extent maps that point to + * unwritten locations are dropped and we don't log them. + */ + if (test_and_clear_bit(BTRFS_INODE_COW_WRITE_ERROR, + &BTRFS_I(inode)->runtime_flags)) + ret = btrfs_wait_ordered_range(inode, start, len); } if (ret) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index c5bdd674f55c..35a413ce935d 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -388,6 +388,37 @@ bool btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered, ret = can_finish_ordered_extent(ordered, page, file_offset, len, uptodate); spin_unlock_irqrestore(&inode->ordered_tree_lock, flags); + /* + * If this is a COW write it means we created new extent maps for the + * range and they point to unwritten locations if we got an error either + * before submitting a bio or during IO. + * + * We have marked the ordered extent with BTRFS_ORDERED_IOERR, and we + * are queuing its completion below. During completion, at + * btrfs_finish_one_ordered(), we will drop the extent maps for the + * unwritten extents. + * + * However because completion runs in a work queue we can end up having + * a fast fsync running before that. In the case of direct IO, once we + * unlock the inode the fsync might start, and we queue the completion + * before unlocking the inode. In the case of buffered IO when writeback + * finishes (end_bbio_data_write()) we queue the completion, so if the + * writeback was triggered by a fast fsync, the fsync might start + * logging before ordered extent completion runs in the work queue. + * + * The fast fsync will log file extent items based on the extent maps it + * finds, so if by the time it collects extent maps the ordered extent + * completion didn't happen yet, it will log file extent items that + * point to unwritten extents, resulting in a corruption if a crash + * happens and the log tree is replayed. Note that a fast fsync does not + * wait for completion of ordered extents in order to reduce latency. + * + * Set a flag in the inode so that the next fast fsync will wait for + * ordered extents to complete before starting to log. + */ + if (!uptodate && !test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags)) + set_bit(BTRFS_INODE_COW_WRITE_ERROR, &inode->runtime_flags); + if (ret) btrfs_queue_ordered_fn(ordered); return ret; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 5146387b416b..26a2e5aa08e9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4860,18 +4860,23 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, path->slots[0]++; continue; } - if (!dropped_extents) { - /* - * Avoid logging extent items logged in past fsync calls - * and leading to duplicate keys in the log tree. - */ + /* + * Avoid overlapping items in the log tree. The first time we + * get here, get rid of everything from a past fsync. After + * that, if the current extent starts before the end of the last + * extent we copied, truncate the last one. This can happen if + * an ordered extent completion modifies the subvolume tree + * while btrfs_next_leaf() has the tree unlocked. + */ + if (!dropped_extents || key.offset < truncate_offset) { ret = truncate_inode_items(trans, root->log_root, inode, - truncate_offset, + min(key.offset, truncate_offset), BTRFS_EXTENT_DATA_KEY); if (ret) goto out; dropped_extents = true; } + truncate_offset = btrfs_file_extent_end(path); if (ins_nr == 0) start_slot = slot; ins_nr++; diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index a002a44ff161..52e50b1b7f22 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -607,7 +607,7 @@ int nilfs_empty_dir(struct inode *inode) kaddr = nilfs_get_folio(inode, i, &folio); if (IS_ERR(kaddr)) - continue; + return 0; de = (struct nilfs_dir_entry *)kaddr; kaddr += nilfs_last_byte(inode, i) - NILFS_DIR_REC_LEN(1); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 60d4f59f7665..6ea81f1d5094 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1652,6 +1652,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) if (bh->b_folio != bd_folio) { if (bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); @@ -1665,6 +1666,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) if (bh == segbuf->sb_super_root) { if (bh->b_folio != bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); @@ -1681,6 +1683,7 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) } if (bd_folio) { folio_lock(bd_folio); + folio_wait_writeback(bd_folio); folio_clear_dirty_for_io(bd_folio); folio_start_writeback(bd_folio); folio_unlock(bd_folio); diff --git a/fs/proc/base.c b/fs/proc/base.c index 18550c071d71..72a1acd03675 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3214,7 +3214,7 @@ static int proc_pid_ksm_stat(struct seq_file *m, struct pid_namespace *ns, mm = get_task_mm(task); if (mm) { seq_printf(m, "ksm_rmap_items %lu\n", mm->ksm_rmap_items); - seq_printf(m, "ksm_zero_pages %lu\n", mm->ksm_zero_pages); + seq_printf(m, "ksm_zero_pages %ld\n", mm_ksm_zero_pages(mm)); seq_printf(m, "ksm_merging_pages %lu\n", mm->ksm_merging_pages); seq_printf(m, "ksm_process_profit %ld\n", ksm_process_profit(mm)); mmput(mm); diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 993ac36c3d58..38a06e8a0f90 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4577,8 +4577,6 @@ smb2_readv_callback(struct mid_q_entry *mid) if (rdata->subreq.start < rdata->subreq.rreq->i_size) rdata->result = 0; } - if (rdata->result == 0 || rdata->result == -EAGAIN) - iov_iter_advance(&rdata->subreq.io_iter, rdata->got_bytes); rdata->credits.value = 0; netfs_subreq_terminated(&rdata->subreq, (rdata->result == 0 || rdata->result == -EAGAIN) ? @@ -4789,7 +4787,6 @@ smb2_writev_callback(struct mid_q_entry *mid) wdata->result = -ENOSPC; else wdata->subreq.len = written; - iov_iter_advance(&wdata->subreq.io_iter, written); break; case MID_REQUEST_SUBMITTED: case MID_RETRY_NEEDED: diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index 02135a605305..1476c445cadc 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -216,8 +216,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) } tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); if (!tcon) { - cifs_put_smb_ses(ses); spin_unlock(&cifs_tcp_ses_lock); + cifs_put_smb_ses(ses); return NULL; } spin_unlock(&cifs_tcp_ses_lock); diff --git a/include/dt-bindings/sound/audio-graph.h b/include/dt-bindings/sound/audio-graph.h new file mode 100644 index 000000000000..bdb70c6b7332 --- /dev/null +++ b/include/dt-bindings/sound/audio-graph.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * audio-graph.h + * + * Copyright (c) 2024 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + */ +#ifndef __AUDIO_GRAPH_H +#define __AUDIO_GRAPH_H + +/* + * used in + * link-trigger-order + * link-trigger-order-start + * link-trigger-order-stop + * + * default is + * link-trigger-order = <SND_SOC_TRIGGER_LINK + * SND_SOC_TRIGGER_COMPONENT + * SND_SOC_TRIGGER_DAI>; + */ +#define SND_SOC_TRIGGER_LINK 0 +#define SND_SOC_TRIGGER_COMPONENT 1 +#define SND_SOC_TRIGGER_DAI 2 +#define SND_SOC_TRIGGER_SIZE 3 /* shoud be last */ + +#endif /* __AUDIO_GRAPH_H */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 28c3fb2bef0d..65e7177bcb02 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -761,6 +761,7 @@ static inline u64 acpi_arch_get_root_pointer(void) } #endif +int acpi_get_local_u64_address(acpi_handle handle, u64 *addr); int acpi_get_local_address(acpi_handle handle, u32 *addr); const char *acpi_get_subsystem_id(acpi_handle handle); diff --git a/include/linux/atomic/atomic-arch-fallback.h b/include/linux/atomic/atomic-arch-fallback.h index 956bcba5dbf2..2f9d36b72bd8 100644 --- a/include/linux/atomic/atomic-arch-fallback.h +++ b/include/linux/atomic/atomic-arch-fallback.h @@ -2242,7 +2242,7 @@ raw_atomic_try_cmpxchg_relaxed(atomic_t *v, int *old, int new) /** * raw_atomic_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: int value to add + * @i: int value to subtract * @v: pointer to atomic_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4368,7 +4368,7 @@ raw_atomic64_try_cmpxchg_relaxed(atomic64_t *v, s64 *old, s64 new) /** * raw_atomic64_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: s64 value to add + * @i: s64 value to subtract * @v: pointer to atomic64_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4690,4 +4690,4 @@ raw_atomic64_dec_if_positive(atomic64_t *v) } #endif /* _LINUX_ATOMIC_FALLBACK_H */ -// 14850c0b0db20c62fdc78ccd1d42b98b88d76331 +// b565db590afeeff0d7c9485ccbca5bb6e155749f diff --git a/include/linux/atomic/atomic-instrumented.h b/include/linux/atomic/atomic-instrumented.h index debd487fe971..9409a6ddf3e0 100644 --- a/include/linux/atomic/atomic-instrumented.h +++ b/include/linux/atomic/atomic-instrumented.h @@ -1349,7 +1349,7 @@ atomic_try_cmpxchg_relaxed(atomic_t *v, int *old, int new) /** * atomic_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: int value to add + * @i: int value to subtract * @v: pointer to atomic_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -2927,7 +2927,7 @@ atomic64_try_cmpxchg_relaxed(atomic64_t *v, s64 *old, s64 new) /** * atomic64_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: s64 value to add + * @i: s64 value to subtract * @v: pointer to atomic64_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -4505,7 +4505,7 @@ atomic_long_try_cmpxchg_relaxed(atomic_long_t *v, long *old, long new) /** * atomic_long_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: long value to add + * @i: long value to subtract * @v: pointer to atomic_long_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -5050,4 +5050,4 @@ atomic_long_dec_if_positive(atomic_long_t *v) #endif /* _LINUX_ATOMIC_INSTRUMENTED_H */ -// ce5b65e0f1f8a276268b667194581d24bed219d4 +// 8829b337928e9508259079d32581775ececd415b diff --git a/include/linux/atomic/atomic-long.h b/include/linux/atomic/atomic-long.h index 3ef844b3ab8a..f86b29d90877 100644 --- a/include/linux/atomic/atomic-long.h +++ b/include/linux/atomic/atomic-long.h @@ -1535,7 +1535,7 @@ raw_atomic_long_try_cmpxchg_relaxed(atomic_long_t *v, long *old, long new) /** * raw_atomic_long_sub_and_test() - atomic subtract and test if zero with full ordering - * @i: long value to add + * @i: long value to subtract * @v: pointer to atomic_long_t * * Atomically updates @v to (@v - @i) with full ordering. @@ -1809,4 +1809,4 @@ raw_atomic_long_dec_if_positive(atomic_long_t *v) } #endif /* _LINUX_ATOMIC_LONG_H */ -// 1c4a26fc77f345342953770ebe3c4d08e7ce2f9a +// eadf183c3600b8b92b91839dd3be6bcc560c752d diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 98c6fd0b39b6..fdfb61ccf55a 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -77,7 +77,7 @@ struct cdrom_device_ops { unsigned int clearing, int slot); int (*tray_move) (struct cdrom_device_info *, int); int (*lock_door) (struct cdrom_device_info *, int); - int (*select_speed) (struct cdrom_device_info *, int); + int (*select_speed) (struct cdrom_device_info *, unsigned long); int (*get_last_session) (struct cdrom_device_info *, struct cdrom_multisession *); int (*get_mcn) (struct cdrom_device_info *, diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 82687e07a7c2..2e649810169f 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -167,7 +167,7 @@ struct cs_dsp { const struct cs_dsp_region *mem; int num_mems; - int fw_ver; + int wmfw_ver; bool booted; bool running; @@ -213,13 +213,13 @@ int cs_dsp_adsp2_init(struct cs_dsp *dsp); int cs_dsp_halo_init(struct cs_dsp *dsp); int cs_dsp_adsp1_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name); void cs_dsp_adsp1_power_down(struct cs_dsp *dsp); int cs_dsp_power_up(struct cs_dsp *dsp, - const struct firmware *wmfw_firmware, char *wmfw_filename, - const struct firmware *coeff_firmware, char *coeff_filename, + const struct firmware *wmfw_firmware, const char *wmfw_filename, + const struct firmware *coeff_firmware, const char *coeff_filename, const char *fw_name); void cs_dsp_power_down(struct cs_dsp *dsp); int cs_dsp_run(struct cs_dsp *dsp); diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h index 5b1d16fa3f56..6e86799a7dc4 100644 --- a/include/linux/firmware/mediatek/mtk-adsp-ipc.h +++ b/include/linux/firmware/mediatek/mtk-adsp-ipc.h @@ -40,7 +40,7 @@ struct mtk_adsp_chan { struct mtk_adsp_ipc { struct mtk_adsp_chan chans[MTK_ADSP_MBOX_NUM]; struct device *dev; - struct mtk_adsp_ipc_ops *ops; + const struct mtk_adsp_ipc_ops *ops; void *private_data; }; diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index c8d3ec116e29..2aa986a5cd1b 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -269,8 +269,8 @@ enum mthp_stat_item { MTHP_STAT_ANON_FAULT_ALLOC, MTHP_STAT_ANON_FAULT_FALLBACK, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE, - MTHP_STAT_ANON_SWPOUT, - MTHP_STAT_ANON_SWPOUT_FALLBACK, + MTHP_STAT_SWPOUT, + MTHP_STAT_SWPOUT_FALLBACK, __MTHP_STAT_COUNT }; @@ -278,6 +278,7 @@ struct mthp_stat { unsigned long stats[ilog2(MAX_PTRS_PER_PTE) + 1][__MTHP_STAT_COUNT]; }; +#ifdef CONFIG_SYSFS DECLARE_PER_CPU(struct mthp_stat, mthp_stats); static inline void count_mthp_stat(int order, enum mthp_stat_item item) @@ -287,6 +288,11 @@ static inline void count_mthp_stat(int order, enum mthp_stat_item item) this_cpu_inc(mthp_stats.stats[order][item]); } +#else +static inline void count_mthp_stat(int order, enum mthp_stat_item item) +{ +} +#endif #define transparent_hugepage_use_zero_page() \ (transparent_hugepage_flags & \ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 5e6cd43a6dbd..9709537370ee 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -852,7 +852,6 @@ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) /* i2c adapter classes (bitmask) */ #define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ -#define I2C_CLASS_SPD (1<<7) /* Memory modules */ /* Warn users that the adapter doesn't support classes anymore */ #define I2C_CLASS_DEPRECATED (1<<8) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 7bc8dff7cf6d..17b3f36ad843 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1533,7 +1533,7 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, static inline struct iommu_sva * iommu_sva_bind_device(struct device *dev, struct mm_struct *mm) { - return NULL; + return ERR_PTR(-ENODEV); } static inline void iommu_sva_unbind_device(struct iommu_sva *handle) diff --git a/include/linux/ksm.h b/include/linux/ksm.h index 52c63a9c5a9c..11690dacd986 100644 --- a/include/linux/ksm.h +++ b/include/linux/ksm.h @@ -33,16 +33,27 @@ void __ksm_exit(struct mm_struct *mm); */ #define is_ksm_zero_pte(pte) (is_zero_pfn(pte_pfn(pte)) && pte_dirty(pte)) -extern unsigned long ksm_zero_pages; +extern atomic_long_t ksm_zero_pages; + +static inline void ksm_map_zero_page(struct mm_struct *mm) +{ + atomic_long_inc(&ksm_zero_pages); + atomic_long_inc(&mm->ksm_zero_pages); +} static inline void ksm_might_unmap_zero_page(struct mm_struct *mm, pte_t pte) { if (is_ksm_zero_pte(pte)) { - ksm_zero_pages--; - mm->ksm_zero_pages--; + atomic_long_dec(&ksm_zero_pages); + atomic_long_dec(&mm->ksm_zero_pages); } } +static inline long mm_ksm_zero_pages(struct mm_struct *mm) +{ + return atomic_long_read(&mm->ksm_zero_pages); +} + static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { if (test_bit(MMF_VM_MERGEABLE, &oldmm->flags)) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 5e51b0de4c4b..08b0d1d9d78b 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -297,9 +297,6 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie); .wait_type_inner = _wait_type, \ .lock_type = LD_LOCK_WAIT_OVERRIDE, } -#define lock_map_assert_held(l) \ - lockdep_assert(lock_is_held(l) != LOCK_STATE_NOT_HELD) - #else /* !CONFIG_LOCKDEP */ static inline void lockdep_init_task(struct task_struct *task) @@ -391,8 +388,6 @@ extern int lockdep_is_held(const void *); #define DEFINE_WAIT_OVERRIDE_MAP(_name, _wait_type) \ struct lockdep_map __maybe_unused _name = {} -#define lock_map_assert_held(l) do { (void)(l); } while (0) - #endif /* !LOCKDEP */ #ifdef CONFIG_PROVE_LOCKING diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 24323c7d0bd4..af3a0256fa93 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -985,7 +985,7 @@ struct mm_struct { * Represent how many empty pages are merged with kernel zero * pages when enabling KSM use_zero_pages. */ - unsigned long ksm_zero_pages; + atomic_long_t ksm_zero_pages; #endif /* CONFIG_KSM */ #ifdef CONFIG_LRU_GEN_WALKS_MMU struct { diff --git a/include/linux/pci.h b/include/linux/pci.h index fb004fd4e889..cafc5ab1cbcb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -413,8 +413,6 @@ struct pci_dev { struct resource driver_exclusive_resource; /* driver exclusive resource ranges */ bool match_driver; /* Skip attaching driver */ - struct lock_class_key cfg_access_key; - struct lockdep_map cfg_access_lock; unsigned int transparent:1; /* Subtractive decode bridge */ unsigned int io_window:1; /* Bridge has I/O window */ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 82561242cda4..7f2ff95d2deb 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -435,8 +435,6 @@ struct pnp_protocol { #define protocol_for_each_dev(protocol, dev) \ list_for_each_entry(dev, &(protocol)->devices, protocol_list) -extern const struct bus_type pnp_bus_type; - #if defined(CONFIG_PNP) /* device management */ @@ -469,7 +467,7 @@ int compare_pnp_id(struct pnp_id *pos, const char *id); int pnp_register_driver(struct pnp_driver *drv); void pnp_unregister_driver(struct pnp_driver *drv); -#define dev_is_pnp(d) ((d)->bus == &pnp_bus_type) +bool dev_is_pnp(const struct device *dev); #else @@ -502,7 +500,7 @@ static inline int compare_pnp_id(struct pnp_id *pos, const char *id) { return -E static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; } static inline void pnp_unregister_driver(struct pnp_driver *drv) { } -#define dev_is_pnp(d) false +static inline bool dev_is_pnp(const struct device *dev) { return false; } #endif /* CONFIG_PNP */ diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 3bfb80bad173..b45d57b5968a 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -13,6 +13,7 @@ enum rtnl_link_flags { RTNL_FLAG_DOIT_UNLOCKED = BIT(0), RTNL_FLAG_BULK_DEL_SUPPORTED = BIT(1), RTNL_FLAG_DUMP_UNLOCKED = BIT(2), + RTNL_FLAG_DUMP_SPLIT_NLM_DONE = BIT(3), /* legacy behavior */ }; enum rtnl_kinds { diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 471e177362b4..5d8e9ed2c005 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -86,7 +86,8 @@ static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key) struct tcp_ao_info { /* List of tcp_ao_key's */ struct hlist_head head; - /* current_key and rnext_key aren't maintained on listen sockets. + /* current_key and rnext_key are maintained on sockets + * in TCP_AO_ESTABLISHED states. * Their purpose is to cache keys on established connections, * saving needless lookups. Never dereference any of them from * listen sockets. @@ -201,9 +202,9 @@ struct tcp6_ao_context { }; struct tcp_sigpool; +/* Established states are fast-path and there always is current_key/rnext_key */ #define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \ - TCPF_CLOSE | TCPF_CLOSE_WAIT | \ - TCPF_LAST_ACK | TCPF_CLOSING) + TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING) int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, struct tcp_ao_key *key, struct tcphdr *th, diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h index 2a333fc1ea81..294e42ea8d4c 100644 --- a/include/soc/fsl/qe/qmc.h +++ b/include/soc/fsl/qe/qmc.h @@ -16,11 +16,32 @@ struct device_node; struct device; struct qmc_chan; -struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); +int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name); + +struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np, + const char *phandles_name, + int index); +struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev, + struct device_node *np, + const char *phandles_name, + int index); + +static inline struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, + const char *phandle_name) +{ + return qmc_chan_get_byphandles_index(np, phandle_name, 0); +} + +static inline struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, + struct device_node *np, + const char *phandle_name) +{ + return devm_qmc_chan_get_byphandles_index(dev, np, phandle_name, 0); +} + struct qmc_chan *qmc_chan_get_bychild(struct device_node *np); void qmc_chan_put(struct qmc_chan *chan); -struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np, - const char *phandle_name); + struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev, struct device_node *np); enum qmc_mode { diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index bb70782d15d0..43c6a9ef8d9f 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -896,8 +896,8 @@ int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap); int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap); int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid); int cs35l41_set_channels(struct device *dev, struct regmap *reg, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg); void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp); int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 1a3c6f66f620..a6aa112e5741 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -80,9 +80,7 @@ #define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044 #define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000 #define CS35L56_DSP1_FW_VER 0x2800010 -#define CS35L56_DSP1_HALO_STATE_A1 0x2801E58 #define CS35L56_DSP1_HALO_STATE 0x28021E0 -#define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000 #define CS35L56_DSP1_PM_CUR_STATE 0x2804308 #define CS35L56_DSP1_XMEM_UNPACKED24_8191 0x2807FFC #define CS35L56_DSP1_CORE_BASE 0x2B80000 @@ -209,7 +207,7 @@ /* CS35L56_MAIN_RENDER_USER_VOLUME */ #define CS35L56_MAIN_RENDER_USER_VOLUME_MIN -400 -#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX 400 +#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX 48 #define CS35L56_MAIN_RENDER_USER_VOLUME_MASK 0x0000FFC0 #define CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT 6 #define CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT 9 @@ -267,13 +265,18 @@ struct cs35l56_base { bool fw_patched; bool secured; bool can_hibernate; - bool fw_owns_asp1; bool cal_data_valid; s8 cal_index; struct cirrus_amp_cal_data cal_data; struct gpio_desc *reset_gpio; }; +/* Temporary to avoid a build break with the HDA driver */ +static inline int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) +{ + return 0; +} + extern struct regmap_config cs35l56_regmap_i2c; extern struct regmap_config cs35l56_regmap_spi; extern struct regmap_config cs35l56_regmap_sdw; @@ -284,8 +287,6 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; int cs35l56_set_patch(struct cs35l56_base *cs35l56_base); -int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base); -int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base); int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base); int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base); diff --git a/include/sound/rt1318.h b/include/sound/rt1318.h new file mode 100644 index 000000000000..fe6bff06036c --- /dev/null +++ b/include/sound/rt1318.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/sound/rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ + +#ifndef __LINUX_SND_RT1318_H +#define __LINUX_SND_RT1318_H + +struct rt1318_platform_data { + unsigned int init_r0_l; + unsigned int init_r0_r; +}; + +#endif diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index ad67957b7b48..3360d9eab068 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -174,6 +174,8 @@ void simple_util_parse_convert(struct device_node *np, char *prefix, struct simple_util_data *data); bool simple_util_is_convert_required(const struct simple_util_data *data); +int simple_util_get_sample_fmt(struct simple_util_data *data); + int simple_util_parse_routing(struct snd_soc_card *card, char *prefix); int simple_util_parse_widgets(struct snd_soc_card *card, @@ -195,8 +197,12 @@ int graph_util_is_ports0(struct device_node *port); int graph_util_parse_dai(struct device *dev, struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link); -int graph_util_parse_link_direction(struct device_node *np, +void graph_util_parse_link_direction(struct device_node *np, bool *is_playback_only, bool *is_capture_only); +void graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + enum snd_soc_trigger_order *trigger_start, + enum snd_soc_trigger_order *trigger_stop); #ifdef DEBUG static inline void simple_util_debug_dai(struct simple_util_priv *priv, diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index adcd8719d343..bbb72ad4c951 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -180,16 +180,16 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); /* Digital Audio interface formatting */ -int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd); -u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority); +int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd); +u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority); int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); @@ -198,11 +198,11 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, int direction); -int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, +int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); -int snd_soc_dai_is_dummy(struct snd_soc_dai *dai); +int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai); int snd_soc_dai_hw_params(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, @@ -218,7 +218,7 @@ void snd_soc_dai_suspend(struct snd_soc_dai *dai); void snd_soc_dai_resume(struct snd_soc_dai *dai); int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num); -bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream); +bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int stream); void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link *dai_link); void snd_soc_dai_action(struct snd_soc_dai *dai, int stream, int action); @@ -232,7 +232,7 @@ static inline void snd_soc_dai_deactivate(struct snd_soc_dai *dai, { snd_soc_dai_action(dai, stream, -1); } -int snd_soc_dai_active(struct snd_soc_dai *dai); +int snd_soc_dai_active(const struct snd_soc_dai *dai); int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order); int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order); @@ -271,7 +271,7 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata); -const char *snd_soc_dai_name_get(struct snd_soc_dai *dai); +const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai); struct snd_soc_dai_ops { /* DAI driver callbacks */ @@ -305,9 +305,9 @@ struct snd_soc_dai_ops { unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width); int (*set_channel_map)(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot); - int (*get_channel_map)(struct snd_soc_dai *dai, + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot); + int (*get_channel_map)(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot); int (*set_tristate)(struct snd_soc_dai *dai, int tristate); @@ -361,7 +361,7 @@ struct snd_soc_dai_ops { * see * snd_soc_dai_get_fmt() */ - u64 *auto_selectable_formats; + const u64 *auto_selectable_formats; int num_auto_selectable_formats; /* probe ordering - for components with runtime dependencies */ @@ -413,7 +413,7 @@ struct snd_soc_dai_driver { unsigned int id; unsigned int base; struct snd_soc_dobj dobj; - struct of_phandle_args *dai_args; + const struct of_phandle_args *dai_args; /* ops */ const struct snd_soc_dai_ops *ops; @@ -473,7 +473,7 @@ struct snd_soc_dai { unsigned int probed:1; }; -static inline struct snd_soc_pcm_stream * +static inline const struct snd_soc_pcm_stream * snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) { return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? @@ -518,7 +518,8 @@ static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *play snd_soc_dai_dma_data_set_capture(dai, capture); } -static inline unsigned int snd_soc_dai_tdm_mask_get(struct snd_soc_dai *dai, int stream) +static inline unsigned int snd_soc_dai_tdm_mask_get(const struct snd_soc_dai *dai, + int stream) { return dai->stream[stream].tdm_mask; } @@ -529,7 +530,8 @@ static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream, dai->stream[stream].tdm_mask = tdm_mask; } -static inline unsigned int snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) +static inline unsigned int snd_soc_dai_stream_active(const struct snd_soc_dai *dai, + int stream) { /* see snd_soc_dai_action() for setup */ return dai->stream[stream].active; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 667ecd4daa68..12cd7b5a2202 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -457,7 +457,7 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uncontrol); int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget, int num); + const struct snd_soc_dapm_widget *widget, unsigned int num); struct snd_soc_dapm_widget *snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget); struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f055c6917f6c..1eedd203ac29 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -178,7 +178,7 @@ static inline const void *snd_soc_tplg_get_data(struct snd_soc_tplg_hdr *hdr) /* Dynamic Object loading and removal for component drivers */ int snd_soc_tplg_component_load(struct snd_soc_component *comp, - struct snd_soc_tplg_ops *ops, const struct firmware *fw); + const struct snd_soc_tplg_ops *ops, const struct firmware *fw); int snd_soc_tplg_component_remove(struct snd_soc_component *comp); /* Binds event handlers to dynamic widgets */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 33671437ee89..a8e66bbf932b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -534,10 +534,10 @@ static inline int snd_soc_set_dmi_name(struct snd_soc_card *card, /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); -int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); +int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params); int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots); -int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); -int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, +int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *parms); +int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params, int tdm_width, int tdm_slots, int slot_multiple); /* set runtime hw params */ @@ -675,7 +675,7 @@ struct snd_soc_dai_link_component { const char *name; struct device_node *of_node; const char *dai_name; - struct of_phandle_args *dai_args; + const struct of_phandle_args *dai_args; }; /* @@ -837,7 +837,8 @@ struct snd_soc_dai_link { #endif }; -static inline int snd_soc_link_num_ch_map(struct snd_soc_dai_link *link) { +static inline int snd_soc_link_num_ch_map(const struct snd_soc_dai_link *link) +{ return max(link->num_cpus, link->num_codecs); } @@ -1299,7 +1300,7 @@ struct soc_enum { #endif }; -static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) +static inline bool snd_soc_volsw_is_stereo(const struct soc_mixer_control *mc) { if (mc->reg == mc->rreg && mc->shift == mc->rshift) return false; @@ -1311,7 +1312,7 @@ static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) return true; } -static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e, +static inline unsigned int snd_soc_enum_val_to_item(const struct soc_enum *e, unsigned int val) { unsigned int i; @@ -1326,7 +1327,7 @@ static inline unsigned int snd_soc_enum_val_to_item(struct soc_enum *e, return 0; } -static inline unsigned int snd_soc_enum_item_to_val(struct soc_enum *e, +static inline unsigned int snd_soc_enum_item_to_val(const struct soc_enum *e, unsigned int item) { if (!e->values) @@ -1401,7 +1402,7 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, snd_soc_daifmt_clock_provider_from_bitmap( \ snd_soc_daifmt_parse_clock_provider_as_bitmap(np, prefix)) -int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream); +int snd_soc_get_stream_cpu(const struct snd_soc_dai_link *dai_link, int stream); int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_component *dlc); int snd_soc_of_get_dlc(struct device_node *of_node, diff --git a/include/sound/sof.h b/include/sound/sof.h index ec6c30d54592..64fd5504cb2b 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -173,5 +173,6 @@ struct sof_dev_desc { int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd); int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd); +int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd); #endif diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index 7fba7ea26a4b..3cda9da14f6d 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -117,10 +117,17 @@ struct tasdevice_fw { struct device *dev; }; -enum tasdevice_dsp_fw_state { - TASDEVICE_DSP_FW_NONE = 0, +enum tasdevice_fw_state { + /* Driver in startup mode, not load any firmware. */ TASDEVICE_DSP_FW_PENDING, + /* DSP firmware in the system, but parsing error. */ TASDEVICE_DSP_FW_FAIL, + /* + * Only RCA (Reconfigurable Architecture) firmware load + * successfully. + */ + TASDEVICE_RCA_FW_OK, + /* Both RCA and DSP firmware load successfully. */ TASDEVICE_DSP_FW_ALL_OK, }; diff --git a/include/sound/tas2781-tlv.h b/include/sound/tas2781-tlv.h index 1dc59005d241..99c41bfc7827 100644 --- a/include/sound/tas2781-tlv.h +++ b/include/sound/tas2781-tlv.h @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated // https://www.ti.com // // The TAS2781 driver implements a flexible and configurable @@ -17,5 +17,265 @@ static const __maybe_unused DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0); static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0); +static const DECLARE_TLV_DB_SCALE(tas2563_dvc_tlv, -12150, 50, 1); +/* pow(10, db/20) * pow(2,30) */ +static const unsigned char tas2563_dvc_table[][4] = { + { 0X00, 0X00, 0X00, 0X00 }, /* -121.5db */ + { 0X00, 0X00, 0X03, 0XBC }, /* -121.0db */ + { 0X00, 0X00, 0X03, 0XF5 }, /* -120.5db */ + { 0X00, 0X00, 0X04, 0X31 }, /* -120.0db */ + { 0X00, 0X00, 0X04, 0X71 }, /* -119.5db */ + { 0X00, 0X00, 0X04, 0XB4 }, /* -119.0db */ + { 0X00, 0X00, 0X04, 0XFC }, /* -118.5db */ + { 0X00, 0X00, 0X05, 0X47 }, /* -118.0db */ + { 0X00, 0X00, 0X05, 0X97 }, /* -117.5db */ + { 0X00, 0X00, 0X05, 0XEC }, /* -117.0db */ + { 0X00, 0X00, 0X06, 0X46 }, /* -116.5db */ + { 0X00, 0X00, 0X06, 0XA5 }, /* -116.0db */ + { 0X00, 0X00, 0X07, 0X0A }, /* -115.5db */ + { 0X00, 0X00, 0X07, 0X75 }, /* -115.0db */ + { 0X00, 0X00, 0X07, 0XE6 }, /* -114.5db */ + { 0X00, 0X00, 0X08, 0X5E }, /* -114.0db */ + { 0X00, 0X00, 0X08, 0XDD }, /* -113.5db */ + { 0X00, 0X00, 0X09, 0X63 }, /* -113.0db */ + { 0X00, 0X00, 0X09, 0XF2 }, /* -112.5db */ + { 0X00, 0X00, 0X0A, 0X89 }, /* -112.0db */ + { 0X00, 0X00, 0X0B, 0X28 }, /* -111.5db */ + { 0X00, 0X00, 0X0B, 0XD2 }, /* -111.0db */ + { 0X00, 0X00, 0X0C, 0X85 }, /* -110.5db */ + { 0X00, 0X00, 0X0D, 0X43 }, /* -110.0db */ + { 0X00, 0X00, 0X0E, 0X0C }, /* -109.5db */ + { 0X00, 0X00, 0X0E, 0XE1 }, /* -109.0db */ + { 0X00, 0X00, 0X0F, 0XC3 }, /* -108.5db */ + { 0X00, 0X00, 0X10, 0XB2 }, /* -108.0db */ + { 0X00, 0X00, 0X11, 0XAF }, /* -107.5db */ + { 0X00, 0X00, 0X12, 0XBC }, /* -107.0db */ + { 0X00, 0X00, 0X13, 0XD8 }, /* -106.5db */ + { 0X00, 0X00, 0X15, 0X05 }, /* -106.0db */ + { 0X00, 0X00, 0X16, 0X44 }, /* -105.5db */ + { 0X00, 0X00, 0X17, 0X96 }, /* -105.0db */ + { 0X00, 0X00, 0X18, 0XFB }, /* -104.5db */ + { 0X00, 0X00, 0X1A, 0X76 }, /* -104.0db */ + { 0X00, 0X00, 0X1C, 0X08 }, /* -103.5db */ + { 0X00, 0X00, 0X1D, 0XB1 }, /* -103.0db */ + { 0X00, 0X00, 0X1F, 0X73 }, /* -102.5db */ + { 0X00, 0X00, 0X21, 0X51 }, /* -102.0db */ + { 0X00, 0X00, 0X23, 0X4A }, /* -101.5db */ + { 0X00, 0X00, 0X25, 0X61 }, /* -101.0db */ + { 0X00, 0X00, 0X27, 0X98 }, /* -100.5db */ + { 0X00, 0X00, 0X29, 0XF1 }, /* -100.0db */ + { 0X00, 0X00, 0X2C, 0X6D }, /* -99.5db */ + { 0X00, 0X00, 0X2F, 0X0F }, /* -99.0db */ + { 0X00, 0X00, 0X31, 0XD9 }, /* -98.5db */ + { 0X00, 0X00, 0X34, 0XCD }, /* -98.0db */ + { 0X00, 0X00, 0X37, 0XEE }, /* -97.5db */ + { 0X00, 0X00, 0X3B, 0X3F }, /* -97.0db */ + { 0X00, 0X00, 0X3E, 0XC1 }, /* -96.5db */ + { 0X00, 0X00, 0X42, 0X79 }, /* -96.0db */ + { 0X00, 0X00, 0X46, 0X6A }, /* -95.5db */ + { 0X00, 0X00, 0X4A, 0X96 }, /* -95.0db */ + { 0X00, 0X00, 0X4F, 0X01 }, /* -94.5db */ + { 0X00, 0X00, 0X53, 0XAF }, /* -94.0db */ + { 0X00, 0X00, 0X58, 0XA5 }, /* -93.5db */ + { 0X00, 0X00, 0X5D, 0XE6 }, /* -93.0db */ + { 0X00, 0X00, 0X63, 0X76 }, /* -92.5db */ + { 0X00, 0X00, 0X69, 0X5B }, /* -92.0db */ + { 0X00, 0X00, 0X6F, 0X99 }, /* -91.5db */ + { 0X00, 0X00, 0X76, 0X36 }, /* -91.0db */ + { 0X00, 0X00, 0X7D, 0X37 }, /* -90.5db */ + { 0X00, 0X00, 0X84, 0XA2 }, /* -90.0db */ + { 0X00, 0X00, 0X8C, 0X7E }, /* -89.5db */ + { 0X00, 0X00, 0X94, 0XD1 }, /* -89.0db */ + { 0X00, 0X00, 0X9D, 0XA3 }, /* -88.5db */ + { 0X00, 0X00, 0XA6, 0XFA }, /* -88.0db */ + { 0X00, 0X00, 0XB0, 0XDF }, /* -87.5db */ + { 0X00, 0X00, 0XBB, 0X5A }, /* -87.0db */ + { 0X00, 0X00, 0XC6, 0X74 }, /* -86.5db */ + { 0X00, 0X00, 0XD2, 0X36 }, /* -86.0db */ + { 0X00, 0X00, 0XDE, 0XAB }, /* -85.5db */ + { 0X00, 0X00, 0XEB, 0XDC }, /* -85.0db */ + { 0X00, 0X00, 0XF9, 0XD6 }, /* -84.5db */ + { 0X00, 0X01, 0X08, 0XA4 }, /* -84.0db */ + { 0X00, 0X01, 0X18, 0X52 }, /* -83.5db */ + { 0X00, 0X01, 0X28, 0XEF }, /* -83.0db */ + { 0X00, 0X01, 0X3A, 0X87 }, /* -82.5db */ + { 0X00, 0X01, 0X4D, 0X2A }, /* -82.0db */ + { 0X00, 0X01, 0X60, 0XE8 }, /* -81.5db */ + { 0X00, 0X01, 0X75, 0XD1 }, /* -81.0db */ + { 0X00, 0X01, 0X8B, 0XF7 }, /* -80.5db */ + { 0X00, 0X01, 0XA3, 0X6E }, /* -80.0db */ + { 0X00, 0X01, 0XBC, 0X48 }, /* -79.5db */ + { 0X00, 0X01, 0XD6, 0X9B }, /* -79.0db */ + { 0X00, 0X01, 0XF2, 0X7E }, /* -78.5db */ + { 0X00, 0X02, 0X10, 0X08 }, /* -78.0db */ + { 0X00, 0X02, 0X2F, 0X51 }, /* -77.5db */ + { 0X00, 0X02, 0X50, 0X76 }, /* -77.0db */ + { 0X00, 0X02, 0X73, 0X91 }, /* -76.5db */ + { 0X00, 0X02, 0X98, 0XC0 }, /* -76.0db */ + { 0X00, 0X02, 0XC0, 0X24 }, /* -75.5db */ + { 0X00, 0X02, 0XE9, 0XDD }, /* -75.0db */ + { 0X00, 0X03, 0X16, 0X0F }, /* -74.5db */ + { 0X00, 0X03, 0X44, 0XDF }, /* -74.0db */ + { 0X00, 0X03, 0X76, 0X76 }, /* -73.5db */ + { 0X00, 0X03, 0XAA, 0XFC }, /* -73.0db */ + { 0X00, 0X03, 0XE2, 0XA0 }, /* -72.5db */ + { 0X00, 0X04, 0X1D, 0X8F }, /* -72.0db */ + { 0X00, 0X04, 0X5B, 0XFD }, /* -71.5db */ + { 0X00, 0X04, 0X9E, 0X1D }, /* -71.0db */ + { 0X00, 0X04, 0XE4, 0X29 }, /* -70.5db */ + { 0X00, 0X05, 0X2E, 0X5A }, /* -70.0db */ + { 0X00, 0X05, 0X7C, 0XF2 }, /* -69.5db */ + { 0X00, 0X05, 0XD0, 0X31 }, /* -69.0db */ + { 0X00, 0X06, 0X28, 0X60 }, /* -68.5db */ + { 0X00, 0X06, 0X85, 0XC8 }, /* -68.0db */ + { 0X00, 0X06, 0XE8, 0XB9 }, /* -67.5db */ + { 0X00, 0X07, 0X51, 0X86 }, /* -67.0db */ + { 0X00, 0X07, 0XC0, 0X8A }, /* -66.5db */ + { 0X00, 0X08, 0X36, 0X21 }, /* -66.0db */ + { 0X00, 0X08, 0XB2, 0XB0 }, /* -65.5db */ + { 0X00, 0X09, 0X36, 0XA1 }, /* -65.0db */ + { 0X00, 0X09, 0XC2, 0X63 }, /* -64.5db */ + { 0X00, 0X0A, 0X56, 0X6D }, /* -64.0db */ + { 0X00, 0X0A, 0XF3, 0X3C }, /* -63.5db */ + { 0X00, 0X0B, 0X99, 0X56 }, /* -63.0db */ + { 0X00, 0X0C, 0X49, 0X48 }, /* -62.5db */ + { 0X00, 0X0D, 0X03, 0XA7 }, /* -62.0db */ + { 0X00, 0X0D, 0XC9, 0X11 }, /* -61.5db */ + { 0X00, 0X0E, 0X9A, 0X2D }, /* -61.0db */ + { 0X00, 0X0F, 0X77, 0XAD }, /* -60.5db */ + { 0X00, 0X10, 0X62, 0X4D }, /* -60.0db */ + { 0X00, 0X11, 0X5A, 0XD5 }, /* -59.5db */ + { 0X00, 0X12, 0X62, 0X16 }, /* -59.0db */ + { 0X00, 0X13, 0X78, 0XF0 }, /* -58.5db */ + { 0X00, 0X14, 0XA0, 0X50 }, /* -58.0db */ + { 0X00, 0X15, 0XD9, 0X31 }, /* -57.5db */ + { 0X00, 0X17, 0X24, 0X9C }, /* -57.0db */ + { 0X00, 0X18, 0X83, 0XAA }, /* -56.5db */ + { 0X00, 0X19, 0XF7, 0X86 }, /* -56.0db */ + { 0X00, 0X1B, 0X81, 0X6A }, /* -55.5db */ + { 0X00, 0X1D, 0X22, 0XA4 }, /* -55.0db */ + { 0X00, 0X1E, 0XDC, 0X98 }, /* -54.5db */ + { 0X00, 0X20, 0XB0, 0XBC }, /* -54.0db */ + { 0X00, 0X22, 0XA0, 0X9D }, /* -53.5db */ + { 0X00, 0X24, 0XAD, 0XE0 }, /* -53.0db */ + { 0X00, 0X26, 0XDA, 0X43 }, /* -52.5db */ + { 0X00, 0X29, 0X27, 0X9D }, /* -52.0db */ + { 0X00, 0X2B, 0X97, 0XE3 }, /* -51.5db */ + { 0X00, 0X2E, 0X2D, 0X27 }, /* -51.0db */ + { 0X00, 0X30, 0XE9, 0X9A }, /* -50.5db */ + { 0X00, 0X33, 0XCF, 0X8D }, /* -50.0db */ + { 0X00, 0X36, 0XE1, 0X78 }, /* -49.5db */ + { 0X00, 0X3A, 0X21, 0XF3 }, /* -49.0db */ + { 0X00, 0X3D, 0X93, 0XC3 }, /* -48.5db */ + { 0X00, 0X41, 0X39, 0XD3 }, /* -48.0db */ + { 0X00, 0X45, 0X17, 0X3B }, /* -47.5db */ + { 0X00, 0X49, 0X2F, 0X44 }, /* -47.0db */ + { 0X00, 0X4D, 0X85, 0X66 }, /* -46.5db */ + { 0X00, 0X52, 0X1D, 0X50 }, /* -46.0db */ + { 0X00, 0X56, 0XFA, 0XE8 }, /* -45.5db */ + { 0X00, 0X5C, 0X22, 0X4E }, /* -45.0db */ + { 0X00, 0X61, 0X97, 0XE1 }, /* -44.5db */ + { 0X00, 0X67, 0X60, 0X44 }, /* -44.0db */ + { 0X00, 0X6D, 0X80, 0X60 }, /* -43.5db */ + { 0X00, 0X73, 0XFD, 0X65 }, /* -43.0db */ + { 0X00, 0X7A, 0XDC, 0XD7 }, /* -42.5db */ + { 0X00, 0X82, 0X24, 0X8A }, /* -42.0db */ + { 0X00, 0X89, 0XDA, 0XAB }, /* -41.5db */ + { 0X00, 0X92, 0X05, 0XC6 }, /* -41.0db */ + { 0X00, 0X9A, 0XAC, 0XC8 }, /* -40.5db */ + { 0X00, 0XA3, 0XD7, 0X0A }, /* -40.0db */ + { 0X00, 0XAD, 0X8C, 0X52 }, /* -39.5db */ + { 0X00, 0XB7, 0XD4, 0XDD }, /* -39.0db */ + { 0X00, 0XC2, 0XB9, 0X65 }, /* -38.5db */ + { 0X00, 0XCE, 0X43, 0X28 }, /* -38.0db */ + { 0X00, 0XDA, 0X7B, 0XF1 }, /* -37.5db */ + { 0X00, 0XE7, 0X6E, 0X1E }, /* -37.0db */ + { 0X00, 0XF5, 0X24, 0XAC }, /* -36.5db */ + { 0X01, 0X03, 0XAB, 0X3D }, /* -36.0db */ + { 0X01, 0X13, 0X0E, 0X24 }, /* -35.5db */ + { 0X01, 0X23, 0X5A, 0X71 }, /* -35.0db */ + { 0X01, 0X34, 0X9D, 0XF8 }, /* -34.5db */ + { 0X01, 0X46, 0XE7, 0X5D }, /* -34.0db */ + { 0X01, 0X5A, 0X46, 0X27 }, /* -33.5db */ + { 0X01, 0X6E, 0XCA, 0XC5 }, /* -33.0db */ + { 0X01, 0X84, 0X86, 0X9F }, /* -32.5db */ + { 0X01, 0X9B, 0X8C, 0X27 }, /* -32.0db */ + { 0X01, 0XB3, 0XEE, 0XE5 }, /* -31.5db */ + { 0X01, 0XCD, 0XC3, 0X8C }, /* -31.0db */ + { 0X01, 0XE9, 0X20, 0X05 }, /* -30.5db */ + { 0X02, 0X06, 0X1B, 0X89 }, /* -30.0db */ + { 0X02, 0X24, 0XCE, 0XB0 }, /* -29.5db */ + { 0X02, 0X45, 0X53, 0X85 }, /* -29.0db */ + { 0X02, 0X67, 0XC5, 0XA2 }, /* -28.5db */ + { 0X02, 0X8C, 0X42, 0X3F }, /* -28.0db */ + { 0X02, 0XB2, 0XE8, 0X55 }, /* -27.5db */ + { 0X02, 0XDB, 0XD8, 0XAD }, /* -27.0db */ + { 0X03, 0X07, 0X36, 0X05 }, /* -26.5db */ + { 0X03, 0X35, 0X25, 0X29 }, /* -26.0db */ + { 0X03, 0X65, 0XCD, 0X13 }, /* -25.5db */ + { 0X03, 0X99, 0X57, 0X0C }, /* -25.0db */ + { 0X03, 0XCF, 0XEE, 0XCF }, /* -24.5db */ + { 0X04, 0X09, 0XC2, 0XB0 }, /* -24.0db */ + { 0X04, 0X47, 0X03, 0XC1 }, /* -23.5db */ + { 0X04, 0X87, 0XE5, 0XFB }, /* -23.0db */ + { 0X04, 0XCC, 0XA0, 0X6D }, /* -22.5db */ + { 0X05, 0X15, 0X6D, 0X68 }, /* -22.0db */ + { 0X05, 0X62, 0X8A, 0XB3 }, /* -21.5db */ + { 0X05, 0XB4, 0X39, 0XBC }, /* -21.0db */ + { 0X06, 0X0A, 0XBF, 0XD4 }, /* -20.5db */ + { 0X06, 0X66, 0X66, 0X66 }, /* -20.0db */ + { 0X06, 0XC7, 0X7B, 0X36 }, /* -19.5db */ + { 0X07, 0X2E, 0X50, 0XA6 }, /* -19.0db */ + { 0X07, 0X9B, 0X3D, 0XF6 }, /* -18.5db */ + { 0X08, 0X0E, 0X9F, 0X96 }, /* -18.0db */ + { 0X08, 0X88, 0XD7, 0X6D }, /* -17.5db */ + { 0X09, 0X0A, 0X4D, 0X2F }, /* -17.0db */ + { 0X09, 0X93, 0X6E, 0XB8 }, /* -16.5db */ + { 0X0A, 0X24, 0XB0, 0X62 }, /* -16.0db */ + { 0X0A, 0XBE, 0X8D, 0X70 }, /* -15.5db */ + { 0X0B, 0X61, 0X88, 0X71 }, /* -15.0db */ + { 0X0C, 0X0E, 0X2B, 0XB0 }, /* -14.5db */ + { 0X0C, 0XC5, 0X09, 0XAB }, /* -14.0db */ + { 0X0D, 0X86, 0XBD, 0X8D }, /* -13.5db */ + { 0X0E, 0X53, 0XEB, 0XB3 }, /* -13.0db */ + { 0X0F, 0X2D, 0X42, 0X38 }, /* -12.5db */ + { 0X10, 0X13, 0X79, 0X87 }, /* -12.0db */ + { 0X11, 0X07, 0X54, 0XF9 }, /* -11.5db */ + { 0X12, 0X09, 0XA3, 0X7A }, /* -11.0db */ + { 0X13, 0X1B, 0X40, 0X39 }, /* -10.5db */ + { 0X14, 0X3D, 0X13, 0X62 }, /* -10.0db */ + { 0X15, 0X70, 0X12, 0XE1 }, /* -9.5db */ + { 0X16, 0XB5, 0X43, 0X37 }, /* -9.0db */ + { 0X18, 0X0D, 0XB8, 0X54 }, /* -8.5db */ + { 0X19, 0X7A, 0X96, 0X7F }, /* -8.0db */ + { 0X1A, 0XFD, 0X13, 0X54 }, /* -7.5db */ + { 0X1C, 0X96, 0X76, 0XC6 }, /* -7.0db */ + { 0X1E, 0X48, 0X1C, 0X37 }, /* -6.5db */ + { 0X20, 0X13, 0X73, 0X9E }, /* -6.0db */ + { 0X21, 0XFA, 0X02, 0XBF }, /* -5.5db */ + { 0X23, 0XFD, 0X66, 0X78 }, /* -5.0db */ + { 0X26, 0X1F, 0X54, 0X1C }, /* -4.5db */ + { 0X28, 0X61, 0X9A, 0XE9 }, /* -4.0db */ + { 0X2A, 0XC6, 0X25, 0X91 }, /* -3.5db */ + { 0X2D, 0X4E, 0XFB, 0XD5 }, /* -3.0db */ + { 0X2F, 0XFE, 0X44, 0X48 }, /* -2.5db */ + { 0X32, 0XD6, 0X46, 0X17 }, /* -2.0db */ + { 0X35, 0XD9, 0X6B, 0X02 }, /* -1.5db */ + { 0X39, 0X0A, 0X41, 0X5F }, /* -1.0db */ + { 0X3C, 0X6B, 0X7E, 0X4F }, /* -0.5db */ + { 0X40, 0X00, 0X00, 0X00 }, /* 0.0db */ + { 0X43, 0XCA, 0XD0, 0X22 }, /* 0.5db */ + { 0X47, 0XCF, 0X26, 0X7D }, /* 1.0db */ + { 0X4C, 0X10, 0X6B, 0XA5 }, /* 1.5db */ + { 0X50, 0X92, 0X3B, 0XE3 }, /* 2.0db */ + { 0X55, 0X58, 0X6A, 0X46 }, /* 2.5db */ + { 0X5A, 0X67, 0X03, 0XDF }, /* 3.0db */ + { 0X5F, 0XC2, 0X53, 0X32 }, /* 3.5db */ + { 0X65, 0X6E, 0XE3, 0XDB }, /* 4.0db */ + { 0X6B, 0X71, 0X86, 0X68 }, /* 4.5db */ + { 0X71, 0XCF, 0X54, 0X71 }, /* 5.0db */ + { 0X78, 0X8D, 0XB4, 0XE9 }, /* 5.5db */ + { 0XFF, 0XFF, 0XFF, 0XFF }, /* 6.0db */ +}; #endif diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 99ca3e401fd1..18161d02a96f 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated // https://www.ti.com // // The TAS2563/TAS2781 driver implements a flexible and configurable @@ -43,13 +43,14 @@ (page * 128)) + reg) /*Software Reset */ -#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01) -#define TAS2781_REG_SWRESET_RESET BIT(0) +#define TASDEVICE_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x01) +#define TASDEVICE_REG_SWRESET_RESET BIT(0) /*I2C Checksum */ #define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E) /* Volume control */ +#define TAS2563_DVC_LVL TASDEVICE_REG(0x00, 0x02, 0x0C) #define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A) #define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) #define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1) @@ -108,6 +109,7 @@ struct tasdevice_priv { unsigned char coef_binaryname[64]; unsigned char rca_binaryname[64]; unsigned char dev_name[32]; + const char *name_prefix; unsigned char ndev; unsigned int magic_num; unsigned int chip_id; @@ -139,7 +141,7 @@ struct tasdevice_priv { void (*apply_calibration)(struct tasdevice_priv *tas_priv); }; -void tas2781_reset(struct tasdevice_priv *tas_dev); +void tasdevice_reset(struct tasdevice_priv *tas_dev); int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, struct module *module, void (*cont)(const struct firmware *fw, void *context)); diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 03edf2ccdf6c..a4206723f503 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -618,6 +618,8 @@ #define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */ #define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */ +#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */ +#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index d1c47a9d9215..7d3316fe9bfc 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -927,7 +927,11 @@ void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work) { struct io_wq_acct *acct = io_work_get_acct(wq, work); unsigned long work_flags = work->flags; - struct io_cb_cancel_data match; + struct io_cb_cancel_data match = { + .fn = io_wq_work_match_item, + .data = work, + .cancel_all = false, + }; bool do_create; /* @@ -965,10 +969,6 @@ void io_wq_enqueue(struct io_wq *wq, struct io_wq_work *work) raw_spin_unlock(&wq->lock); /* fatal condition, failed to create the first worker */ - match.fn = io_wq_work_match_item, - match.data = work, - match.cancel_all = false, - io_acct_cancel_pending_work(wq, acct, &match); } } diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 624ca9076a50..726e6367af4d 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -433,7 +433,7 @@ static inline bool io_file_can_poll(struct io_kiocb *req) { if (req->flags & REQ_F_CAN_POLL) return true; - if (file_can_poll(req->file)) { + if (req->file && file_can_poll(req->file)) { req->flags |= REQ_F_CAN_POLL; return true; } diff --git a/io_uring/napi.c b/io_uring/napi.c index 883a1a665907..8c18ede595c4 100644 --- a/io_uring/napi.c +++ b/io_uring/napi.c @@ -261,12 +261,14 @@ int io_unregister_napi(struct io_ring_ctx *ctx, void __user *arg) } /* - * __io_napi_adjust_timeout() - Add napi id to the busy poll list + * __io_napi_adjust_timeout() - adjust busy loop timeout * @ctx: pointer to io-uring context structure * @iowq: pointer to io wait queue * @ts: pointer to timespec or NULL * * Adjust the busy loop timeout according to timespec and busy poll timeout. + * If the specified NAPI timeout is bigger than the wait timeout, then adjust + * the NAPI timeout accordingly. */ void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iowq, struct timespec64 *ts) @@ -274,16 +276,16 @@ void __io_napi_adjust_timeout(struct io_ring_ctx *ctx, struct io_wait_queue *iow unsigned int poll_to = READ_ONCE(ctx->napi_busy_poll_to); if (ts) { - struct timespec64 poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to); - - if (timespec64_compare(ts, &poll_to_ts) > 0) { - *ts = timespec64_sub(*ts, poll_to_ts); - } else { - u64 to = timespec64_to_ns(ts); - - do_div(to, 1000); - ts->tv_sec = 0; - ts->tv_nsec = 0; + struct timespec64 poll_to_ts; + + poll_to_ts = ns_to_timespec64(1000 * (s64)poll_to); + if (timespec64_compare(ts, &poll_to_ts) < 0) { + s64 poll_to_ns = timespec64_to_ns(ts); + if (poll_to_ns > 0) { + u64 val = poll_to_ns + 999; + do_div(val, (s64) 1000); + poll_to = val; + } } } diff --git a/io_uring/register.c b/io_uring/register.c index ef8c908346a4..c0010a66a6f2 100644 --- a/io_uring/register.c +++ b/io_uring/register.c @@ -355,8 +355,10 @@ static __cold int io_register_iowq_max_workers(struct io_ring_ctx *ctx, } if (sqd) { + mutex_unlock(&ctx->uring_lock); mutex_unlock(&sqd->lock); io_put_sq_data(sqd); + mutex_lock(&ctx->uring_lock); } if (copy_to_user(arg, new_count, sizeof(new_count))) @@ -380,8 +382,10 @@ static __cold int io_register_iowq_max_workers(struct io_ring_ctx *ctx, return 0; err: if (sqd) { + mutex_unlock(&ctx->uring_lock); mutex_unlock(&sqd->lock); io_put_sq_data(sqd); + mutex_lock(&ctx->uring_lock); } return ret; } diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 4e2cdbb5629f..7f3b34452243 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -760,9 +760,6 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, for (i = 0; i < dtab->n_buckets; i++) { head = dev_map_index_hash(dtab, i); hlist_for_each_entry_safe(dst, next, head, index_hlist) { - if (!dst) - continue; - if (is_ifindex_excluded(excluded_devices, num_excluded, dst->dev->ifindex)) continue; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2222c3ff88e7..f45ed6adc092 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2998,6 +2998,7 @@ static int bpf_obj_get(const union bpf_attr *attr) void bpf_link_init(struct bpf_link *link, enum bpf_link_type type, const struct bpf_link_ops *ops, struct bpf_prog *prog) { + WARN_ON(ops->dealloc && ops->dealloc_deferred); atomic64_set(&link->refcnt, 1); link->type = type; link->id = 0; @@ -3056,16 +3057,17 @@ static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu) /* bpf_link_free is guaranteed to be called from process context */ static void bpf_link_free(struct bpf_link *link) { + const struct bpf_link_ops *ops = link->ops; bool sleepable = false; bpf_link_free_id(link->id); if (link->prog) { sleepable = link->prog->sleepable; /* detach BPF program, clean up used resources */ - link->ops->release(link); + ops->release(link); bpf_prog_put(link->prog); } - if (link->ops->dealloc_deferred) { + if (ops->dealloc_deferred) { /* schedule BPF link deallocation; if underlying BPF program * is sleepable, we need to first wait for RCU tasks trace * sync, then go through "classic" RCU grace period @@ -3074,9 +3076,8 @@ static void bpf_link_free(struct bpf_link *link) call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp); else call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp); - } - if (link->ops->dealloc) - link->ops->dealloc(link); + } else if (ops->dealloc) + ops->dealloc(link); } static void bpf_link_put_deferred(struct work_struct *work) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 48f3a9acdef3..36ef8e96787e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11128,7 +11128,11 @@ BTF_ID(func, bpf_iter_css_task_new) #else BTF_ID_UNUSED #endif +#ifdef CONFIG_BPF_EVENTS BTF_ID(func, bpf_session_cookie) +#else +BTF_ID_UNUSED +#endif static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) { diff --git a/kernel/events/core.c b/kernel/events/core.c index f0128c5ff278..8f908f077935 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5384,6 +5384,7 @@ int perf_event_release_kernel(struct perf_event *event) again: mutex_lock(&event->child_mutex); list_for_each_entry(child, &event->child_list, child_list) { + void *var = NULL; /* * Cannot change, child events are not migrated, see the @@ -5424,11 +5425,23 @@ again: * this can't be the last reference. */ put_event(event); + } else { + var = &ctx->refcount; } mutex_unlock(&event->child_mutex); mutex_unlock(&ctx->mutex); put_ctx(ctx); + + if (var) { + /* + * If perf_event_free_task() has deleted all events from the + * ctx while the child_mutex got released above, make sure to + * notify about the preceding put_ctx(). + */ + smp_mb(); /* pairs with wait_var_event() */ + wake_up_var(var); + } goto again; } mutex_unlock(&event->child_mutex); diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 6249dac61701..d1daeab1bbc1 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -3517,7 +3517,6 @@ static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx) } #endif /* CONFIG_UPROBES */ -#ifdef CONFIG_FPROBE __bpf_kfunc_start_defs(); __bpf_kfunc bool bpf_session_is_return(void) @@ -3566,4 +3565,3 @@ static int __init bpf_kprobe_multi_kfuncs_init(void) } late_initcall(bpf_kprobe_multi_kfuncs_init); -#endif diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 42b585208249..c63db03ebb9d 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -811,4 +811,5 @@ static void __exit test_rht_exit(void) module_init(test_rht_init); module_exit(test_rht_exit); +MODULE_DESCRIPTION("Resizable, Scalable, Concurrent Hash Table test module"); MODULE_LICENSE("GPL v2"); diff --git a/mm/filemap.c b/mm/filemap.c index 382c3d06bfb1..876cc64aadd7 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1000,7 +1000,7 @@ struct folio *filemap_alloc_folio_noprof(gfp_t gfp, unsigned int order) do { cpuset_mems_cookie = read_mems_allowed_begin(); n = cpuset_mem_spread_node(); - folio = __folio_alloc_node(gfp, order, n); + folio = __folio_alloc_node_noprof(gfp, order, n); } while (!folio && read_mems_allowed_retry(cpuset_mems_cookie)); return folio; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 317de2afd371..89932fd0f62e 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -558,15 +558,15 @@ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) DEFINE_MTHP_STAT_ATTR(anon_fault_alloc, MTHP_STAT_ANON_FAULT_ALLOC); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback, MTHP_STAT_ANON_FAULT_FALLBACK); DEFINE_MTHP_STAT_ATTR(anon_fault_fallback_charge, MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE); -DEFINE_MTHP_STAT_ATTR(anon_swpout, MTHP_STAT_ANON_SWPOUT); -DEFINE_MTHP_STAT_ATTR(anon_swpout_fallback, MTHP_STAT_ANON_SWPOUT_FALLBACK); +DEFINE_MTHP_STAT_ATTR(swpout, MTHP_STAT_SWPOUT); +DEFINE_MTHP_STAT_ATTR(swpout_fallback, MTHP_STAT_SWPOUT_FALLBACK); static struct attribute *stats_attrs[] = { &anon_fault_alloc_attr.attr, &anon_fault_fallback_attr.attr, &anon_fault_fallback_charge_attr.attr, - &anon_swpout_attr.attr, - &anon_swpout_fallback_attr.attr, + &swpout_attr.attr, + &swpout_fallback_attr.attr, NULL, }; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 6be78e7d4f6e..f35abff8be60 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5768,8 +5768,20 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, * do_exit() will not see it, and will keep the reservation * forever. */ - if (adjust_reservation && vma_needs_reservation(h, vma, address)) - vma_add_reservation(h, vma, address); + if (adjust_reservation) { + int rc = vma_needs_reservation(h, vma, address); + + if (rc < 0) + /* Pressumably allocate_file_region_entries failed + * to allocate a file_region struct. Clear + * hugetlb_restore_reserve so that global reserve + * count will not be incremented by free_huge_folio. + * Act as if we consumed the reservation. + */ + folio_clear_hugetlb_restore_reserve(page_folio(page)); + else if (rc) + vma_add_reservation(h, vma, address); + } tlb_remove_page_size(tlb, page, huge_page_size(h)); /* diff --git a/mm/kmsan/core.c b/mm/kmsan/core.c index cf2d70e9c9a5..95f859e38c53 100644 --- a/mm/kmsan/core.c +++ b/mm/kmsan/core.c @@ -196,8 +196,7 @@ void kmsan_internal_set_shadow_origin(void *addr, size_t size, int b, u32 origin, bool checked) { u64 address = (u64)addr; - void *shadow_start; - u32 *origin_start; + u32 *shadow_start, *origin_start; size_t pad = 0; KMSAN_WARN_ON(!kmsan_metadata_is_contiguous(addr, size)); @@ -225,8 +224,16 @@ void kmsan_internal_set_shadow_origin(void *addr, size_t size, int b, origin_start = (u32 *)kmsan_get_metadata((void *)address, KMSAN_META_ORIGIN); - for (int i = 0; i < size / KMSAN_ORIGIN_SIZE; i++) - origin_start[i] = origin; + /* + * If the new origin is non-zero, assume that the shadow byte is also non-zero, + * and unconditionally overwrite the old origin slot. + * If the new origin is zero, overwrite the old origin slot iff the + * corresponding shadow slot is zero. + */ + for (int i = 0; i < size / KMSAN_ORIGIN_SIZE; i++) { + if (origin || !shadow_start[i]) + origin_start[i] = origin; + } } struct page *kmsan_vmalloc_to_page_or_null(void *vaddr) @@ -296,7 +296,7 @@ static bool ksm_use_zero_pages __read_mostly; static bool ksm_smart_scan = true; /* The number of zero pages which is placed by KSM */ -unsigned long ksm_zero_pages; +atomic_long_t ksm_zero_pages = ATOMIC_LONG_INIT(0); /* The number of pages that have been skipped due to "smart scanning" */ static unsigned long ksm_pages_skipped; @@ -1429,8 +1429,7 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, * the dirty bit in zero page's PTE is set. */ newpte = pte_mkdirty(pte_mkspecial(pfn_pte(page_to_pfn(kpage), vma->vm_page_prot))); - ksm_zero_pages++; - mm->ksm_zero_pages++; + ksm_map_zero_page(mm); /* * We're replacing an anonymous page with a zero page, which is * not anonymous. We need to do proper accounting otherwise we @@ -2754,18 +2753,16 @@ static void ksm_do_scan(unsigned int scan_npages) { struct ksm_rmap_item *rmap_item; struct page *page; - unsigned int npages = scan_npages; - while (npages-- && likely(!freezing(current))) { + while (scan_npages-- && likely(!freezing(current))) { cond_resched(); rmap_item = scan_get_next_rmap_item(&page); if (!rmap_item) return; cmp_and_merge_page(page, rmap_item); put_page(page); + ksm_pages_scanned++; } - - ksm_pages_scanned += scan_npages - npages; } static int ksmd_should_run(void) @@ -3376,7 +3373,7 @@ static void wait_while_offlining(void) #ifdef CONFIG_PROC_FS long ksm_process_profit(struct mm_struct *mm) { - return (long)(mm->ksm_merging_pages + mm->ksm_zero_pages) * PAGE_SIZE - + return (long)(mm->ksm_merging_pages + mm_ksm_zero_pages(mm)) * PAGE_SIZE - mm->ksm_rmap_items * sizeof(struct ksm_rmap_item); } #endif /* CONFIG_PROC_FS */ @@ -3665,7 +3662,7 @@ KSM_ATTR_RO(pages_skipped); static ssize_t ksm_zero_pages_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sysfs_emit(buf, "%ld\n", ksm_zero_pages); + return sysfs_emit(buf, "%ld\n", atomic_long_read(&ksm_zero_pages)); } KSM_ATTR_RO(ksm_zero_pages); @@ -3674,7 +3671,7 @@ static ssize_t general_profit_show(struct kobject *kobj, { long general_profit; - general_profit = (ksm_pages_sharing + ksm_zero_pages) * PAGE_SIZE - + general_profit = (ksm_pages_sharing + atomic_long_read(&ksm_zero_pages)) * PAGE_SIZE - ksm_rmap_items * sizeof(struct ksm_rmap_item); return sysfs_emit(buf, "%ld\n", general_profit); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7fad15b2290c..36793e509f47 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3147,8 +3147,6 @@ static inline void __mod_objcg_mlstate(struct obj_cgroup *objcg, struct mem_cgroup *memcg; struct lruvec *lruvec; - lockdep_assert_irqs_disabled(); - rcu_read_lock(); memcg = obj_cgroup_memcg(objcg); lruvec = mem_cgroup_lruvec(memcg, pgdat); diff --git a/mm/mempool.c b/mm/mempool.c index 6ece63a00acf..3223337135d0 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -273,7 +273,7 @@ mempool_t *mempool_create_node_noprof(int min_nr, mempool_alloc_t *alloc_fn, { mempool_t *pool; - pool = kzalloc_node(sizeof(*pool), gfp_mask, node_id); + pool = kmalloc_node_noprof(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id); if (!pool) return NULL; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2e22ce5675ca..222299b5c0e6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1955,10 +1955,12 @@ int find_suitable_fallback(struct free_area *area, unsigned int order, } /* - * Reserve a pageblock for exclusive use of high-order atomic allocations if - * there are no empty page blocks that contain a page with a suitable order + * Reserve the pageblock(s) surrounding an allocation request for + * exclusive use of high-order atomic allocations if there are no + * empty page blocks that contain a page with a suitable order */ -static void reserve_highatomic_pageblock(struct page *page, struct zone *zone) +static void reserve_highatomic_pageblock(struct page *page, int order, + struct zone *zone) { int mt; unsigned long max_managed, flags; @@ -1984,10 +1986,17 @@ static void reserve_highatomic_pageblock(struct page *page, struct zone *zone) /* Yoink! */ mt = get_pageblock_migratetype(page); /* Only reserve normal pageblocks (i.e., they can merge with others) */ - if (migratetype_is_mergeable(mt)) - if (move_freepages_block(zone, page, mt, - MIGRATE_HIGHATOMIC) != -1) - zone->nr_reserved_highatomic += pageblock_nr_pages; + if (!migratetype_is_mergeable(mt)) + goto out_unlock; + + if (order < pageblock_order) { + if (move_freepages_block(zone, page, mt, MIGRATE_HIGHATOMIC) == -1) + goto out_unlock; + zone->nr_reserved_highatomic += pageblock_nr_pages; + } else { + change_pageblock_range(page, order, MIGRATE_HIGHATOMIC); + zone->nr_reserved_highatomic += 1 << order; + } out_unlock: spin_unlock_irqrestore(&zone->lock, flags); @@ -1999,7 +2008,7 @@ out_unlock: * intense memory pressure but failed atomic allocations should be easier * to recover from than an OOM. * - * If @force is true, try to unreserve a pageblock even though highatomic + * If @force is true, try to unreserve pageblocks even though highatomic * pageblock is exhausted. */ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, @@ -2041,6 +2050,7 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * adjust the count once. */ if (is_migrate_highatomic(mt)) { + unsigned long size; /* * It should never happen but changes to * locking could inadvertently allow a per-cpu @@ -2048,9 +2058,9 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * while unreserving so be safe and watch for * underflows. */ - zone->nr_reserved_highatomic -= min( - pageblock_nr_pages, - zone->nr_reserved_highatomic); + size = max(pageblock_nr_pages, 1UL << order); + size = min(size, zone->nr_reserved_highatomic); + zone->nr_reserved_highatomic -= size; } /* @@ -2062,11 +2072,19 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac, * of pageblocks that cannot be completely freed * may increase. */ - ret = move_freepages_block(zone, page, mt, - ac->migratetype); + if (order < pageblock_order) + ret = move_freepages_block(zone, page, mt, + ac->migratetype); + else { + move_to_free_list(page, zone, order, mt, + ac->migratetype); + change_pageblock_range(page, order, + ac->migratetype); + ret = 1; + } /* - * Reserving this block already succeeded, so this should - * not fail on zone boundaries. + * Reserving the block(s) already succeeded, + * so this should not fail on zone boundaries. */ WARN_ON_ONCE(ret == -1); if (ret > 0) { @@ -3406,7 +3424,7 @@ try_this_zone: * if the pageblock should be reserved for the future */ if (unlikely(alloc_flags & ALLOC_HIGHATOMIC)) - reserve_highatomic_pageblock(page, zone); + reserve_highatomic_pageblock(page, order, zone); return page; } else { diff --git a/mm/page_io.c b/mm/page_io.c index 46c603dddf04..0a150c240bf4 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -217,7 +217,7 @@ static inline void count_swpout_vm_event(struct folio *folio) count_memcg_folio_events(folio, THP_SWPOUT, 1); count_vm_event(THP_SWPOUT); } - count_mthp_stat(folio_order(folio), MTHP_STAT_ANON_SWPOUT); + count_mthp_stat(folio_order(folio), MTHP_STAT_SWPOUT); #endif count_vm_events(PSWPOUT, folio_nr_pages(folio)); } diff --git a/mm/slub.c b/mm/slub.c index 0809760cf789..1373ac365a46 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1952,7 +1952,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, #ifdef CONFIG_MEMCG new_exts |= MEMCG_DATA_OBJEXTS; #endif - old_exts = slab->obj_exts; + old_exts = READ_ONCE(slab->obj_exts); handle_failed_objexts_alloc(old_exts, vec, objects); if (new_slab) { /* @@ -1961,7 +1961,8 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, * be simply assigned. */ slab->obj_exts = new_exts; - } else if (cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { + } else if ((old_exts & ~OBJEXTS_FLAGS_MASK) || + cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { /* * If the slab is already in use, somebody can allocate and * assign slabobj_exts in parallel. In this case the existing diff --git a/mm/util.c b/mm/util.c index c9e519e6811f..6c3e6710e4de 100644 --- a/mm/util.c +++ b/mm/util.c @@ -705,7 +705,7 @@ void *kvrealloc_noprof(const void *p, size_t oldsize, size_t newsize, gfp_t flag if (oldsize >= newsize) return (void *)p; - newp = kvmalloc(newsize, flags); + newp = kvmalloc_noprof(newsize, flags); if (!newp) return NULL; memcpy(newp, p, oldsize); @@ -726,7 +726,7 @@ void *__vmalloc_array_noprof(size_t n, size_t size, gfp_t flags) if (unlikely(check_mul_overflow(n, size, &bytes))) return NULL; - return __vmalloc(bytes, flags); + return __vmalloc_noprof(bytes, flags); } EXPORT_SYMBOL(__vmalloc_array_noprof); @@ -737,7 +737,7 @@ EXPORT_SYMBOL(__vmalloc_array_noprof); */ void *vmalloc_array_noprof(size_t n, size_t size) { - return __vmalloc_array(n, size, GFP_KERNEL); + return __vmalloc_array_noprof(n, size, GFP_KERNEL); } EXPORT_SYMBOL(vmalloc_array_noprof); @@ -749,7 +749,7 @@ EXPORT_SYMBOL(vmalloc_array_noprof); */ void *__vcalloc_noprof(size_t n, size_t size, gfp_t flags) { - return __vmalloc_array(n, size, flags | __GFP_ZERO); + return __vmalloc_array_noprof(n, size, flags | __GFP_ZERO); } EXPORT_SYMBOL(__vcalloc_noprof); @@ -760,7 +760,7 @@ EXPORT_SYMBOL(__vcalloc_noprof); */ void *vcalloc_noprof(size_t n, size_t size) { - return __vmalloc_array(n, size, GFP_KERNEL | __GFP_ZERO); + return __vmalloc_array_noprof(n, size, GFP_KERNEL | __GFP_ZERO); } EXPORT_SYMBOL(vcalloc_noprof); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5d3aa2dc88a8..45e1506d58c3 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -722,7 +722,7 @@ int is_vmalloc_or_module_addr(const void *x) * and fall back on vmalloc() if that fails. Others * just put it in the vmalloc space. */ -#if defined(CONFIG_MODULES) && defined(MODULES_VADDR) +#if defined(CONFIG_EXECMEM) && defined(MODULES_VADDR) unsigned long addr = (unsigned long)kasan_reset_tag(x); if (addr >= MODULES_VADDR && addr < MODULES_END) return 1; diff --git a/mm/vmscan.c b/mm/vmscan.c index d55e8d07ffc4..2e34de9cd0d4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1227,7 +1227,7 @@ retry: THP_SWPOUT_FALLBACK, 1); count_vm_event(THP_SWPOUT_FALLBACK); } - count_mthp_stat(order, MTHP_STAT_ANON_SWPOUT_FALLBACK); + count_mthp_stat(order, MTHP_STAT_SWPOUT_FALLBACK); #endif if (!add_to_swap(folio)) goto activate_locked_split; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 8077cf2ee448..d6f9fae06a9d 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1378,8 +1378,10 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, { struct sk_buff *skb; struct sock *newsk; + ax25_dev *ax25_dev; DEFINE_WAIT(wait); struct sock *sk; + ax25_cb *ax25; int err = 0; if (sock->state != SS_UNCONNECTED) @@ -1434,6 +1436,10 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, kfree_skb(skb); sk_acceptq_removed(sk); newsock->state = SS_CONNECTED; + ax25 = sk_to_ax25(newsk); + ax25_dev = ax25->ax25_dev; + netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC); + ax25_dev_hold(ax25_dev); out: release_sock(sk); diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index 742d7c68e7e7..9efd6690b344 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -196,7 +196,7 @@ void __exit ax25_dev_free(void) list_for_each_entry_safe(s, n, &ax25_dev_list, list) { netdev_put(s->dev, &s->dev_tracker); list_del(&s->list); - kfree(s); + ax25_dev_put(s); } spin_unlock_bh(&ax25_dev_lock); } diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index f6aad4ed2ab2..36ae54f57bf5 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -727,10 +727,16 @@ static void __bpf_prog_test_run_raw_tp(void *data) { struct bpf_raw_tp_test_run_info *info = data; + struct bpf_trace_run_ctx run_ctx = {}; + struct bpf_run_ctx *old_run_ctx; + + old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); rcu_read_lock(); info->retval = bpf_prog_run(info->prog, info->ctx); rcu_read_unlock(); + + bpf_reset_run_ctx(old_run_ctx); } int bpf_prog_test_run_raw_tp(struct bpf_prog *prog, diff --git a/net/core/dev.c b/net/core/dev.c index e1bb6d7856d9..4d4de9008f6f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4516,12 +4516,13 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb, struct rps_dev_flow *rflow, u16 next_cpu) { if (next_cpu < nr_cpu_ids) { + u32 head; #ifdef CONFIG_RFS_ACCEL struct netdev_rx_queue *rxqueue; struct rps_dev_flow_table *flow_table; struct rps_dev_flow *old_rflow; - u32 flow_id, head; u16 rxq_index; + u32 flow_id; int rc; /* Should we steer this flow to a different hardware queue? */ diff --git a/net/core/dst_cache.c b/net/core/dst_cache.c index 6a0482e676d3..70c634b9e7b0 100644 --- a/net/core/dst_cache.c +++ b/net/core/dst_cache.c @@ -27,6 +27,7 @@ struct dst_cache_pcpu { static void dst_cache_per_cpu_dst_set(struct dst_cache_pcpu *dst_cache, struct dst_entry *dst, u32 cookie) { + DEBUG_NET_WARN_ON_ONCE(!in_softirq()); dst_release(dst_cache->dst); if (dst) dst_hold(dst); @@ -40,6 +41,7 @@ static struct dst_entry *dst_cache_per_cpu_get(struct dst_cache *dst_cache, { struct dst_entry *dst; + DEBUG_NET_WARN_ON_ONCE(!in_softirq()); dst = idst->dst; if (!dst) goto fail; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b86b0a87367d..4668d6718040 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -6484,6 +6484,46 @@ static int rtnl_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, /* Process one rtnetlink message. */ +static int rtnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb) +{ + rtnl_dumpit_func dumpit = cb->data; + int err; + + /* Previous iteration have already finished, avoid calling->dumpit() + * again, it may not expect to be called after it reached the end. + */ + if (!dumpit) + return 0; + + err = dumpit(skb, cb); + + /* Old dump handlers used to send NLM_DONE as in a separate recvmsg(). + * Some applications which parse netlink manually depend on this. + */ + if (cb->flags & RTNL_FLAG_DUMP_SPLIT_NLM_DONE) { + if (err < 0 && err != -EMSGSIZE) + return err; + if (!err) + cb->data = NULL; + + return skb->len; + } + return err; +} + +static int rtnetlink_dump_start(struct sock *ssk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + struct netlink_dump_control *control) +{ + if (control->flags & RTNL_FLAG_DUMP_SPLIT_NLM_DONE) { + WARN_ON(control->data); + control->data = control->dump; + control->dump = rtnl_dumpit; + } + + return netlink_dump_start(ssk, skb, nlh, control); +} + static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -6548,7 +6588,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, .module = owner, .flags = flags, }; - err = netlink_dump_start(rtnl, skb, nlh, &c); + err = rtnetlink_dump_start(rtnl, skb, nlh, &c); /* netlink_dump_start() will keep a reference on * module if dump is still in progress. */ @@ -6694,7 +6734,7 @@ void __init rtnetlink_init(void) register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, - rtnl_dump_ifinfo, 0); + rtnl_dump_ifinfo, RTNL_FLAG_DUMP_SPLIT_NLM_DONE); rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, 0); rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0); rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, 0); diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 5a55270aa86e..e645d751a5e8 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2220,7 +2220,7 @@ static int ethtool_get_phy_stats_ethtool(struct net_device *dev, const struct ethtool_ops *ops = dev->ethtool_ops; int n_stats, ret; - if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats) + if (!ops || !ops->get_sset_count || !ops->get_ethtool_phy_stats) return -EOPNOTSUPP; n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS); diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index be2755c8d8fd..57d496287e52 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -38,11 +38,11 @@ static int tsinfo_prepare_data(const struct ethnl_req_info *req_base, ret = ethnl_ops_begin(dev); if (ret < 0) return ret; - if (req_base->flags & ETHTOOL_FLAG_STATS && - dev->ethtool_ops->get_ts_stats) { + if (req_base->flags & ETHTOOL_FLAG_STATS) { ethtool_stats_init((u64 *)&data->stats, sizeof(data->stats) / sizeof(u64)); - dev->ethtool_ops->get_ts_stats(dev, &data->stats); + if (dev->ethtool_ops->get_ts_stats) + dev->ethtool_ops->get_ts_stats(dev, &data->stats); } ret = __ethtool_get_ts_info(dev, &data->ts_info); ethnl_ops_complete(dev); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f3892ee9dfb3..d09f557eaa77 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2805,7 +2805,7 @@ void __init devinet_init(void) rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, - RTNL_FLAG_DUMP_UNLOCKED); + RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE); rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, inet_netconf_dump_devconf, RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c484b1c0fc00..7ad2cafb9276 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1050,11 +1050,6 @@ next: e++; } } - - /* Don't let NLM_DONE coalesce into a message, even if it could. - * Some user space expects NLM_DONE in a separate recv(). - */ - err = skb->len; out: cb->args[1] = e; @@ -1665,5 +1660,5 @@ void __init ip_fib_init(void) rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0); rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0); rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, - RTNL_FLAG_DUMP_UNLOCKED); + RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 681b54e1f3a6..e6790ea74877 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1165,6 +1165,9 @@ new_segment: process_backlog++; +#ifdef CONFIG_SKB_DECRYPTED + skb->decrypted = !!(flags & MSG_SENDPAGE_DECRYPTED); +#endif tcp_skb_entail(sk, skb); copy = size_goal; @@ -2646,6 +2649,10 @@ void tcp_set_state(struct sock *sk, int state) if (oldstate != TCP_ESTABLISHED) TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); break; + case TCP_CLOSE_WAIT: + if (oldstate == TCP_SYN_RECV) + TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); + break; case TCP_CLOSE: if (oldstate == TCP_CLOSE_WAIT || oldstate == TCP_ESTABLISHED) @@ -2657,7 +2664,7 @@ void tcp_set_state(struct sock *sk, int state) inet_put_port(sk); fallthrough; default: - if (oldstate == TCP_ESTABLISHED) + if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT) TCP_DEC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 781b67a52571..37c42b63ff99 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -933,6 +933,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, struct tcp_ao_key *key; __be32 sisn, disn; u8 *traffic_key; + int state; u32 sne = 0; info = rcu_dereference(tcp_sk(sk)->ao_info); @@ -948,8 +949,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, disn = 0; } + state = READ_ONCE(sk->sk_state); /* Fast-path */ - if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) { + if (likely((1 << state) & TCP_AO_ESTABLISHED)) { enum skb_drop_reason err; struct tcp_ao_key *current_key; @@ -988,6 +990,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, return SKB_NOT_DROPPED_YET; } + if (unlikely(state == TCP_CLOSE)) + return SKB_DROP_REASON_TCP_CLOSE; + /* Lookup key based on peer address and keyid. * current_key and rnext_key must not be used on tcp listen * sockets as otherwise: @@ -1001,7 +1006,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, if (th->syn && !th->ack) goto verify_hash; - if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { + if ((1 << state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { /* Make the initial syn the likely case here */ if (unlikely(req)) { sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, @@ -1018,14 +1023,14 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, /* no way to figure out initial sisn/disn - drop */ return SKB_DROP_REASON_TCP_FLAGS; } - } else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { + } else if ((1 << state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { disn = info->lisn; if (th->syn || th->rst) sisn = th->seq; else sisn = info->risn; } else { - WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); + WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", state); return SKB_DROP_REASON_TCP_AOFAILURE; } verify_hash: diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 0601bad79822..ff7e734e335b 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -58,7 +58,9 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) return orig_dst->lwtstate->orig_output(net, sk, skb); } + local_bh_disable(); dst = dst_cache_get(&ilwt->dst_cache); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *ip6h = ipv6_hdr(skb); struct flowi6 fl6; @@ -86,8 +88,11 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } - if (ilwt->connected) + if (ilwt->connected) { + local_bh_disable(); dst_cache_set_ip6(&ilwt->dst_cache, dst, &fl6.saddr); + local_bh_enable(); + } } skb_dst_set(skb, dst); diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index 7563f8c6aa87..bf7120ecea1e 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -351,9 +351,9 @@ do_encap: goto drop; if (!ipv6_addr_equal(&orig_daddr, &ipv6_hdr(skb)->daddr)) { - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&ilwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -373,9 +373,9 @@ do_encap: goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&ilwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 31d77885bcae..6e57c03e3255 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -966,6 +966,7 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, if (!fib6_nh->rt6i_pcpu) return; + rcu_read_lock(); /* release the reference to this fib entry from * all of its cached pcpu routes */ @@ -974,7 +975,9 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, struct rt6_info *pcpu_rt; ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); - pcpu_rt = *ppcpu_rt; + + /* Paired with xchg() in rt6_get_pcpu_route() */ + pcpu_rt = READ_ONCE(*ppcpu_rt); /* only dropping the 'from' reference if the cached route * is using 'match'. The cached pcpu_rt->from only changes @@ -988,6 +991,7 @@ static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, fib6_info_release(from); } } + rcu_read_unlock(); } struct fib6_nh_pcpu_arg { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a504b88ec06b..f083d9faba6b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1409,6 +1409,7 @@ static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res) struct rt6_info *prev, **p; p = this_cpu_ptr(res->nh->rt6i_pcpu); + /* Paired with READ_ONCE() in __fib6_drop_pcpu_from() */ prev = xchg(p, NULL); if (prev) { dst_dev_put(&prev->dst); diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c index a013b92cbb86..2c83b7586422 100644 --- a/net/ipv6/rpl_iptunnel.c +++ b/net/ipv6/rpl_iptunnel.c @@ -212,9 +212,9 @@ static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (unlikely(err)) goto drop; - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&rlwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -234,9 +234,9 @@ static int rpl_output(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&rlwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); @@ -268,23 +268,21 @@ static int rpl_input(struct sk_buff *skb) return err; } - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&rlwt->cache); - preempt_enable(); if (!dst) { ip6_route_input(skb); dst = skb_dst(skb); if (!dst->error) { - preempt_disable(); dst_cache_set_ip6(&rlwt->cache, dst, &ipv6_hdr(skb)->saddr); - preempt_enable(); } } else { skb_dst_drop(skb); skb_dst_set(skb, dst); } + local_bh_enable(); err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); if (unlikely(err)) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index a75df2ec8db0..098632adc9b5 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -464,23 +464,21 @@ static int seg6_input_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&slwt->cache); - preempt_enable(); if (!dst) { ip6_route_input(skb); dst = skb_dst(skb); if (!dst->error) { - preempt_disable(); dst_cache_set_ip6(&slwt->cache, dst, &ipv6_hdr(skb)->saddr); - preempt_enable(); } } else { skb_dst_drop(skb); skb_dst_set(skb, dst); } + local_bh_enable(); err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); if (unlikely(err)) @@ -536,9 +534,9 @@ static int seg6_output_core(struct net *net, struct sock *sk, slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); - preempt_disable(); + local_bh_disable(); dst = dst_cache_get(&slwt->cache); - preempt_enable(); + local_bh_enable(); if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -558,9 +556,9 @@ static int seg6_output_core(struct net *net, struct sock *sk, goto drop; } - preempt_disable(); + local_bh_disable(); dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); - preempt_enable(); + local_bh_enable(); } skb_dst_drop(skb); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b08e5d7687e3..83ad6c9709fe 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2958,8 +2958,9 @@ static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(int) * NUM_NL80211_BANDS); - ieee80211_link_info_change_notify(sdata, &sdata->deflink, - BSS_CHANGED_MCAST_RATE); + if (ieee80211_sdata_running(sdata)) + ieee80211_link_info_change_notify(sdata, &sdata->deflink, + BSS_CHANGED_MCAST_RATE); return 0; } @@ -4016,7 +4017,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } - link_data->csa_chanreq = chanreq; + link_data->csa_chanreq = chanreq; link_conf->csa_active = true; if (params->block_tx && @@ -4027,7 +4028,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, } cfg80211_ch_switch_started_notify(sdata->dev, - &link_data->csa_chanreq.oper, 0, + &link_data->csa_chanreq.oper, link_id, params->count, params->block_tx); if (changed) { diff --git a/net/mac80211/he.c b/net/mac80211/he.c index 9f5ffdc9db28..ecbb042dd043 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -230,15 +230,21 @@ ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif, if (!he_spr_ie_elem) return; + + he_obss_pd->sr_ctrl = he_spr_ie_elem->he_sr_control; data = he_spr_ie_elem->optional; if (he_spr_ie_elem->he_sr_control & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) - data++; + he_obss_pd->non_srg_max_offset = *data++; + if (he_spr_ie_elem->he_sr_control & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) { - he_obss_pd->max_offset = *data++; he_obss_pd->min_offset = *data++; + he_obss_pd->max_offset = *data++; + memcpy(he_obss_pd->bss_color_bitmap, data, 8); + data += 8; + memcpy(he_obss_pd->partial_bssid_bitmap, data, 8); he_obss_pd->enable = true; } } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index eb62b7d4b4f7..3cedfdc9099b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1845,6 +1845,8 @@ void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_configure_filter(struct ieee80211_local *local); u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); +void ieee80211_handle_queued_frames(struct ieee80211_local *local); + u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local); int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, u64 *cookie, gfp_t gfp); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 4eaea0a9975b..1132dea0e290 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -423,9 +423,8 @@ u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_ERP_SLOT; } -static void ieee80211_tasklet_handler(struct tasklet_struct *t) +void ieee80211_handle_queued_frames(struct ieee80211_local *local) { - struct ieee80211_local *local = from_tasklet(local, t, tasklet); struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -450,6 +449,13 @@ static void ieee80211_tasklet_handler(struct tasklet_struct *t) } } +static void ieee80211_tasklet_handler(struct tasklet_struct *t) +{ + struct ieee80211_local *local = from_tasklet(local, t, tasklet); + + ieee80211_handle_queued_frames(local); +} + static void ieee80211_restart_work(struct work_struct *work) { struct ieee80211_local *local = diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index cbc9b5e40cb3..6d4510221c98 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1776,6 +1776,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) ifmsh->last_preq = jiffies; ifmsh->next_perr = jiffies; ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; + ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index a6b62169f084..c0a5c75cddcb 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -1017,10 +1017,23 @@ void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, */ void mesh_path_flush_pending(struct mesh_path *mpath) { + struct ieee80211_sub_if_data *sdata = mpath->sdata; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_preq_queue *preq, *tmp; struct sk_buff *skb; while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) mesh_path_discard_frame(mpath->sdata, skb); + + spin_lock_bh(&ifmsh->mesh_preq_queue_lock); + list_for_each_entry_safe(preq, tmp, &ifmsh->preq_queue.list, list) { + if (ether_addr_equal(mpath->dst, preq->dst)) { + list_del(&preq->list); + kfree(preq); + --ifmsh->preq_queue_len; + } + } + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); } /** diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 55e5497f8978..055a60e90979 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -111,7 +111,7 @@ ieee80211_parse_extension_element(u32 *crc, if (params->mode < IEEE80211_CONN_MODE_HE) break; if (len >= sizeof(*elems->he_spr) && - len >= ieee80211_he_spr_size(data)) + len >= ieee80211_he_spr_size(data) - 1) elems->he_spr = data; break; case WLAN_EID_EXT_HE_6GHZ_CAPA: diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 3da1c5c45035..8ecc4b710b0e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -744,15 +744,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->hw_scan_ies_bufsize *= n_bands; } - local->hw_scan_req = kmalloc( - sizeof(*local->hw_scan_req) + - req->n_channels * sizeof(req->channels[0]) + - local->hw_scan_ies_bufsize, GFP_KERNEL); + local->hw_scan_req = kmalloc(struct_size(local->hw_scan_req, + req.channels, + req->n_channels) + + local->hw_scan_ies_bufsize, + GFP_KERNEL); if (!local->hw_scan_req) return -ENOMEM; local->hw_scan_req->req.ssids = req->ssids; local->hw_scan_req->req.n_ssids = req->n_ssids; + /* None of the channels are actually set + * up but let UBSAN know the boundaries. + */ + local->hw_scan_req->req.n_channels = req->n_channels; + ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index da5fdd6f5c85..aa22f09e6d14 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1724,7 +1724,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) skb_queue_head_init(&pending); /* sync with ieee80211_tx_h_unicast_ps_buf */ - spin_lock(&sta->ps_lock); + spin_lock_bh(&sta->ps_lock); /* Send all buffered frames to the station */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { int count = skb_queue_len(&pending), tmp; @@ -1753,7 +1753,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) */ clear_sta_flag(sta, WLAN_STA_PSPOLL); clear_sta_flag(sta, WLAN_STA_UAPSD); - spin_unlock(&sta->ps_lock); + spin_unlock_bh(&sta->ps_lock); atomic_dec(&ps->num_sta_ps); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0b893e958959..283bfc99417e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1567,6 +1567,8 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, void ieee80211_stop_device(struct ieee80211_local *local) { + ieee80211_handle_queued_frames(local); + ieee80211_led_radio(local, false); ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 7d44196ec5b6..96b113854bd3 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2916,9 +2916,14 @@ void mptcp_set_state(struct sock *sk, int state) if (oldstate != TCP_ESTABLISHED) MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB); break; - + case TCP_CLOSE_WAIT: + /* Unlike TCP, MPTCP sk would not have the TCP_SYN_RECV state: + * MPTCP "accepted" sockets will be created later on. So no + * transition from TCP_SYN_RECV to TCP_CLOSE_WAIT. + */ + break; default: - if (oldstate == TCP_ESTABLISHED) + if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT) MPTCP_DEC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB); } diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h index 374412ed780b..ef0f8f73826f 100644 --- a/net/ncsi/internal.h +++ b/net/ncsi/internal.h @@ -325,6 +325,7 @@ struct ncsi_dev_priv { spinlock_t lock; /* Protect the NCSI device */ unsigned int package_probe_id;/* Current ID during probe */ unsigned int package_num; /* Number of packages */ + unsigned int channel_probe_id;/* Current cahnnel ID during probe */ struct list_head packages; /* List of packages */ struct ncsi_channel *hot_channel; /* Channel was ever active */ struct ncsi_request requests[256]; /* Request table */ @@ -343,6 +344,7 @@ struct ncsi_dev_priv { bool multi_package; /* Enable multiple packages */ bool mlx_multi_host; /* Enable multi host Mellanox */ u32 package_whitelist; /* Packages to configure */ + unsigned char channel_count; /* Num of channels to probe */ }; struct ncsi_cmd_arg { diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 745c788f1d1d..5ecf611c8820 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -510,17 +510,19 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) break; case ncsi_dev_state_suspend_gls: - ndp->pending_req_num = np->channel_num; + ndp->pending_req_num = 1; nca.type = NCSI_PKT_CMD_GLS; nca.package = np->id; + nca.channel = ndp->channel_probe_id; + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + ndp->channel_probe_id++; - nd->state = ncsi_dev_state_suspend_dcnt; - NCSI_FOR_EACH_CHANNEL(np, nc) { - nca.channel = nc->id; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; + if (ndp->channel_probe_id == ndp->channel_count) { + ndp->channel_probe_id = 0; + nd->state = ncsi_dev_state_suspend_dcnt; } break; @@ -1345,7 +1347,6 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) { struct ncsi_dev *nd = &ndp->ndev; struct ncsi_package *np; - struct ncsi_channel *nc; struct ncsi_cmd_arg nca; unsigned char index; int ret; @@ -1423,23 +1424,6 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nd->state = ncsi_dev_state_probe_cis; break; - case ncsi_dev_state_probe_cis: - ndp->pending_req_num = NCSI_RESERVED_CHANNEL; - - /* Clear initial state */ - nca.type = NCSI_PKT_CMD_CIS; - nca.package = ndp->active_package->id; - for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) { - nca.channel = index; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; - } - - nd->state = ncsi_dev_state_probe_gvi; - if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_KEEP_PHY)) - nd->state = ncsi_dev_state_probe_keep_phy; - break; case ncsi_dev_state_probe_keep_phy: ndp->pending_req_num = 1; @@ -1452,14 +1436,17 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nd->state = ncsi_dev_state_probe_gvi; break; + case ncsi_dev_state_probe_cis: case ncsi_dev_state_probe_gvi: case ncsi_dev_state_probe_gc: case ncsi_dev_state_probe_gls: np = ndp->active_package; - ndp->pending_req_num = np->channel_num; + ndp->pending_req_num = 1; - /* Retrieve version, capability or link status */ - if (nd->state == ncsi_dev_state_probe_gvi) + /* Clear initial state Retrieve version, capability or link status */ + if (nd->state == ncsi_dev_state_probe_cis) + nca.type = NCSI_PKT_CMD_CIS; + else if (nd->state == ncsi_dev_state_probe_gvi) nca.type = NCSI_PKT_CMD_GVI; else if (nd->state == ncsi_dev_state_probe_gc) nca.type = NCSI_PKT_CMD_GC; @@ -1467,19 +1454,29 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) nca.type = NCSI_PKT_CMD_GLS; nca.package = np->id; - NCSI_FOR_EACH_CHANNEL(np, nc) { - nca.channel = nc->id; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; - } + nca.channel = ndp->channel_probe_id; - if (nd->state == ncsi_dev_state_probe_gvi) + ret = ncsi_xmit_cmd(&nca); + if (ret) + goto error; + + if (nd->state == ncsi_dev_state_probe_cis) { + nd->state = ncsi_dev_state_probe_gvi; + if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_KEEP_PHY) && ndp->channel_probe_id == 0) + nd->state = ncsi_dev_state_probe_keep_phy; + } else if (nd->state == ncsi_dev_state_probe_gvi) { nd->state = ncsi_dev_state_probe_gc; - else if (nd->state == ncsi_dev_state_probe_gc) + } else if (nd->state == ncsi_dev_state_probe_gc) { nd->state = ncsi_dev_state_probe_gls; - else + } else { + nd->state = ncsi_dev_state_probe_cis; + ndp->channel_probe_id++; + } + + if (ndp->channel_probe_id == ndp->channel_count) { + ndp->channel_probe_id = 0; nd->state = ncsi_dev_state_probe_dp; + } break; case ncsi_dev_state_probe_dp: ndp->pending_req_num = 1; @@ -1780,6 +1777,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, ndp->requests[i].ndp = ndp; timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0); } + ndp->channel_count = NCSI_RESERVED_CHANNEL; spin_lock_irqsave(&ncsi_dev_lock, flags); list_add_tail_rcu(&ndp->node, &ncsi_dev_list); @@ -1813,6 +1811,7 @@ int ncsi_start_dev(struct ncsi_dev *nd) if (!(ndp->flags & NCSI_DEV_PROBED)) { ndp->package_probe_id = 0; + ndp->channel_probe_id = 0; nd->state = ncsi_dev_state_probe; schedule_work(&ndp->work); return 0; diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c index bee290d0f48b..e28be33bdf2c 100644 --- a/net/ncsi/ncsi-rsp.c +++ b/net/ncsi/ncsi-rsp.c @@ -795,12 +795,13 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr) struct ncsi_rsp_gc_pkt *rsp; struct ncsi_dev_priv *ndp = nr->ndp; struct ncsi_channel *nc; + struct ncsi_package *np; size_t size; /* Find the channel */ rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp); ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, - NULL, &nc); + &np, &nc); if (!nc) return -ENODEV; @@ -835,6 +836,7 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr) */ nc->vlan_filter.bitmap = U64_MAX; nc->vlan_filter.n_vids = rsp->vlan_cnt; + np->ndp->channel_count = rsp->channel_cnt; return 0; } diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 79e93a19d5fa..06e03f5cd7ce 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -185,7 +185,7 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt, qopt->bands = qdisc_dev(sch)->real_num_tx_queues; - removed = kmalloc(sizeof(*removed) * (q->max_bands - q->bands), + removed = kmalloc(sizeof(*removed) * (q->max_bands - qopt->bands), GFP_KERNEL); if (!removed) return -ENOMEM; diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 937a0c513c17..b284a06b5a75 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1176,16 +1176,13 @@ static int taprio_parse_mqprio_opt(struct net_device *dev, { bool allow_overlapping_txqs = TXTIME_ASSIST_IS_ENABLED(taprio_flags); - if (!qopt && !dev->num_tc) { - NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary"); - return -EINVAL; - } - - /* If num_tc is already set, it means that the user already - * configured the mqprio part - */ - if (dev->num_tc) + if (!qopt) { + if (!dev->num_tc) { + NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary"); + return -EINVAL; + } return 0; + } /* taprio imposes that traffic classes map 1:n to tx queues */ if (qopt->num_tc > dev->num_tx_queues) { diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index e50a286fd0fb..c5f98c6b2561 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -459,29 +459,11 @@ out: static void smc_adjust_sock_bufsizes(struct sock *nsk, struct sock *osk, unsigned long mask) { - struct net *nnet = sock_net(nsk); - nsk->sk_userlocks = osk->sk_userlocks; - if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) { + if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) nsk->sk_sndbuf = osk->sk_sndbuf; - } else { - if (mask == SK_FLAGS_SMC_TO_CLC) - WRITE_ONCE(nsk->sk_sndbuf, - READ_ONCE(nnet->ipv4.sysctl_tcp_wmem[1])); - else - WRITE_ONCE(nsk->sk_sndbuf, - 2 * READ_ONCE(nnet->smc.sysctl_wmem)); - } - if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) { + if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) nsk->sk_rcvbuf = osk->sk_rcvbuf; - } else { - if (mask == SK_FLAGS_SMC_TO_CLC) - WRITE_ONCE(nsk->sk_rcvbuf, - READ_ONCE(nnet->ipv4.sysctl_tcp_rmem[1])); - else - WRITE_ONCE(nsk->sk_rcvbuf, - 2 * READ_ONCE(nnet->smc.sysctl_rmem)); - } } static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 96ab50eda9c2..73a90ad873fb 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1069,7 +1069,7 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp, goto out_denied_free; pages = DIV_ROUND_UP(inlen, PAGE_SIZE); - in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); + in_token->pages = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL); if (!in_token->pages) goto out_denied_free; in_token->page_base = 0; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 25b49efc0926..80846279de9f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -221,15 +221,9 @@ static inline int unix_may_send(struct sock *sk, struct sock *osk) return unix_peer(osk) == NULL || unix_our_peer(sk, osk); } -static inline int unix_recvq_full(const struct sock *sk) -{ - return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog; -} - static inline int unix_recvq_full_lockless(const struct sock *sk) { - return skb_queue_len_lockless(&sk->sk_receive_queue) > - READ_ONCE(sk->sk_max_ack_backlog); + return skb_queue_len_lockless(&sk->sk_receive_queue) > sk->sk_max_ack_backlog; } struct sock *unix_peer_get(struct sock *s) @@ -530,10 +524,10 @@ static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) return 0; } -static int unix_writable(const struct sock *sk) +static int unix_writable(const struct sock *sk, unsigned char state) { - return sk->sk_state != TCP_LISTEN && - (refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; + return state != TCP_LISTEN && + (refcount_read(&sk->sk_wmem_alloc) << 2) <= READ_ONCE(sk->sk_sndbuf); } static void unix_write_space(struct sock *sk) @@ -541,7 +535,7 @@ static void unix_write_space(struct sock *sk) struct socket_wq *wq; rcu_read_lock(); - if (unix_writable(sk)) { + if (unix_writable(sk, READ_ONCE(sk->sk_state))) { wq = rcu_dereference(sk->sk_wq); if (skwq_has_sleeper(wq)) wake_up_interruptible_sync_poll(&wq->wait, @@ -570,7 +564,6 @@ static void unix_dgram_disconnected(struct sock *sk, struct sock *other) sk_error_report(other); } } - other->sk_state = TCP_CLOSE; } static void unix_sock_destructor(struct sock *sk) @@ -617,7 +610,7 @@ static void unix_release_sock(struct sock *sk, int embrion) u->path.dentry = NULL; u->path.mnt = NULL; state = sk->sk_state; - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); skpair = unix_peer(sk); unix_peer(sk) = NULL; @@ -638,7 +631,7 @@ static void unix_release_sock(struct sock *sk, int embrion) unix_state_lock(skpair); /* No more writes */ WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK); - if (!skb_queue_empty(&sk->sk_receive_queue) || embrion) + if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || embrion) WRITE_ONCE(skpair->sk_err, ECONNRESET); unix_state_unlock(skpair); skpair->sk_state_change(skpair); @@ -739,7 +732,8 @@ static int unix_listen(struct socket *sock, int backlog) if (backlog > sk->sk_max_ack_backlog) wake_up_interruptible_all(&u->peer_wait); sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; + WRITE_ONCE(sk->sk_state, TCP_LISTEN); + /* set credentials so connect can copy them */ init_peercred(sk); err = 0; @@ -976,7 +970,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern, sk->sk_hash = unix_unbound_hash(sk); sk->sk_allocation = GFP_KERNEL_ACCOUNT; sk->sk_write_space = unix_write_space; - sk->sk_max_ack_backlog = net->unx.sysctl_max_dgram_qlen; + sk->sk_max_ack_backlog = READ_ONCE(net->unx.sysctl_max_dgram_qlen); sk->sk_destruct = unix_sock_destructor; u = unix_sk(sk); u->listener = NULL; @@ -1402,7 +1396,8 @@ restart: if (err) goto out_unlock; - sk->sk_state = other->sk_state = TCP_ESTABLISHED; + WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED); + WRITE_ONCE(other->sk_state, TCP_ESTABLISHED); } else { /* * 1003.1g breaking connected state with AF_UNSPEC @@ -1419,13 +1414,20 @@ restart: unix_peer(sk) = other; if (!other) - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer); unix_state_double_unlock(sk, other); - if (other != old_peer) + if (other != old_peer) { unix_dgram_disconnected(sk, old_peer); + + unix_state_lock(old_peer); + if (!unix_peer(old_peer)) + WRITE_ONCE(old_peer->sk_state, TCP_CLOSE); + unix_state_unlock(old_peer); + } + sock_put(old_peer); } else { unix_peer(sk) = other; @@ -1473,7 +1475,6 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, struct sk_buff *skb = NULL; long timeo; int err; - int st; err = unix_validate_addr(sunaddr, addr_len); if (err) @@ -1538,7 +1539,7 @@ restart: if (other->sk_shutdown & RCV_SHUTDOWN) goto out_unlock; - if (unix_recvq_full(other)) { + if (unix_recvq_full_lockless(other)) { err = -EAGAIN; if (!timeo) goto out_unlock; @@ -1563,9 +1564,7 @@ restart: Well, and we have to recheck the state after socket locked. */ - st = sk->sk_state; - - switch (st) { + switch (READ_ONCE(sk->sk_state)) { case TCP_CLOSE: /* This is ok... continue with connect */ break; @@ -1580,7 +1579,7 @@ restart: unix_state_lock_nested(sk, U_LOCK_SECOND); - if (sk->sk_state != st) { + if (sk->sk_state != TCP_CLOSE) { unix_state_unlock(sk); unix_state_unlock(other); sock_put(other); @@ -1633,7 +1632,7 @@ restart: copy_peercred(sk, other); sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; + WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED); sock_hold(newsk); smp_mb__after_atomic(); /* sock_hold() does an atomic_inc() */ @@ -1705,7 +1704,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, goto out; arg->err = -EINVAL; - if (sk->sk_state != TCP_LISTEN) + if (READ_ONCE(sk->sk_state) != TCP_LISTEN) goto out; /* If socket state is TCP_LISTEN it cannot change (for now...), @@ -1962,7 +1961,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, } err = -EMSGSIZE; - if (len > sk->sk_sndbuf - 32) + if (len > READ_ONCE(sk->sk_sndbuf) - 32) goto out; if (len > SKB_MAX_ALLOC) { @@ -2044,7 +2043,7 @@ restart_locked: unix_peer(sk) = NULL; unix_dgram_peer_wake_disconnect_wakeup(sk, other); - sk->sk_state = TCP_CLOSE; + WRITE_ONCE(sk->sk_state, TCP_CLOSE); unix_state_unlock(sk); unix_dgram_disconnected(sk, other); @@ -2221,7 +2220,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, } if (msg->msg_namelen) { - err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; + err = READ_ONCE(sk->sk_state) == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; goto out_err; } else { err = -ENOTCONN; @@ -2242,7 +2241,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, &err, 0); } else { /* Keep two messages in the pipe so it schedules better */ - size = min_t(int, size, (sk->sk_sndbuf >> 1) - 64); + size = min_t(int, size, (READ_ONCE(sk->sk_sndbuf) >> 1) - 64); /* allow fallback to order-0 allocations */ size = min_t(int, size, SKB_MAX_HEAD(0) + UNIX_SKB_FRAGS_SZ); @@ -2335,7 +2334,7 @@ static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg, if (err) return err; - if (sk->sk_state != TCP_ESTABLISHED) + if (READ_ONCE(sk->sk_state) != TCP_ESTABLISHED) return -ENOTCONN; if (msg->msg_namelen) @@ -2349,7 +2348,7 @@ static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg, { struct sock *sk = sock->sk; - if (sk->sk_state != TCP_ESTABLISHED) + if (READ_ONCE(sk->sk_state) != TCP_ESTABLISHED) return -ENOTCONN; return unix_dgram_recvmsg(sock, msg, size, flags); @@ -2654,7 +2653,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) { - if (unlikely(sk->sk_state != TCP_ESTABLISHED)) + if (unlikely(READ_ONCE(sk->sk_state) != TCP_ESTABLISHED)) return -ENOTCONN; return unix_read_skb(sk, recv_actor); @@ -2678,7 +2677,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, size_t size = state->size; unsigned int last_len; - if (unlikely(sk->sk_state != TCP_ESTABLISHED)) { + if (unlikely(READ_ONCE(sk->sk_state) != TCP_ESTABLISHED)) { err = -EINVAL; goto out; } @@ -3009,7 +3008,7 @@ long unix_inq_len(struct sock *sk) struct sk_buff *skb; long amount = 0; - if (sk->sk_state == TCP_LISTEN) + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) return -EINVAL; spin_lock(&sk->sk_receive_queue.lock); @@ -3121,12 +3120,14 @@ static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned lon static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; + unsigned char state; __poll_t mask; u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; shutdown = READ_ONCE(sk->sk_shutdown); + state = READ_ONCE(sk->sk_state); /* exceptional events? */ if (READ_ONCE(sk->sk_err)) @@ -3148,14 +3149,14 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa /* Connection-based need to check for termination and startup */ if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) && - sk->sk_state == TCP_CLOSE) + state == TCP_CLOSE) mask |= EPOLLHUP; /* * we set writable also when the other side has shut down the * connection. This prevents stuck sockets. */ - if (unix_writable(sk)) + if (unix_writable(sk, state)) mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; return mask; @@ -3166,12 +3167,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, { struct sock *sk = sock->sk, *other; unsigned int writable; + unsigned char state; __poll_t mask; u8 shutdown; sock_poll_wait(file, sock, wait); mask = 0; shutdown = READ_ONCE(sk->sk_shutdown); + state = READ_ONCE(sk->sk_state); /* exceptional events? */ if (READ_ONCE(sk->sk_err) || @@ -3191,19 +3194,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, mask |= EPOLLIN | EPOLLRDNORM; /* Connection-based need to check for termination and startup */ - if (sk->sk_type == SOCK_SEQPACKET) { - if (sk->sk_state == TCP_CLOSE) - mask |= EPOLLHUP; - /* connection hasn't started yet? */ - if (sk->sk_state == TCP_SYN_SENT) - return mask; - } + if (sk->sk_type == SOCK_SEQPACKET && state == TCP_CLOSE) + mask |= EPOLLHUP; /* No write status requested, avoid expensive OUT tests. */ if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT))) return mask; - writable = unix_writable(sk); + writable = unix_writable(sk, state); if (writable) { unix_state_lock(sk); diff --git a/net/unix/diag.c b/net/unix/diag.c index ae39538c5042..937edf4afed4 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -65,7 +65,7 @@ static int sk_diag_dump_icons(struct sock *sk, struct sk_buff *nlskb) u32 *buf; int i; - if (sk->sk_state == TCP_LISTEN) { + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) { spin_lock(&sk->sk_receive_queue.lock); attr = nla_reserve(nlskb, UNIX_DIAG_ICONS, @@ -103,8 +103,8 @@ static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb) { struct unix_diag_rqlen rql; - if (sk->sk_state == TCP_LISTEN) { - rql.udiag_rqueue = sk->sk_receive_queue.qlen; + if (READ_ONCE(sk->sk_state) == TCP_LISTEN) { + rql.udiag_rqueue = skb_queue_len_lockless(&sk->sk_receive_queue); rql.udiag_wqueue = sk->sk_max_ack_backlog; } else { rql.udiag_rqueue = (u32) unix_inq_len(sk); @@ -136,7 +136,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r rep = nlmsg_data(nlh); rep->udiag_family = AF_UNIX; rep->udiag_type = sk->sk_type; - rep->udiag_state = sk->sk_state; + rep->udiag_state = READ_ONCE(sk->sk_state); rep->pad = 0; rep->udiag_ino = sk_ino; sock_diag_save_cookie(sk, rep->udiag_cookie); @@ -165,7 +165,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r sock_diag_put_meminfo(sk, skb, UNIX_DIAG_MEMINFO)) goto out_nlmsg_trim; - if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, sk->sk_shutdown)) + if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, READ_ONCE(sk->sk_shutdown))) goto out_nlmsg_trim; if ((req->udiag_show & UDIAG_SHOW_UID) && @@ -215,7 +215,7 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) sk_for_each(sk, &net->unx.table.buckets[slot]) { if (num < s_num) goto next; - if (!(req->udiag_states & (1 << sk->sk_state))) + if (!(req->udiag_states & (1 << READ_ONCE(sk->sk_state)))) goto next; if (sk_diag_dump(sk, skb, req, sk_user_ns(skb->sk), NETLINK_CB(cb->skb).portid, diff --git a/net/wireless/core.c b/net/wireless/core.c index 3fb1b637352a..4b1f45e3070e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -431,7 +431,7 @@ static void cfg80211_wiphy_work(struct work_struct *work) if (wk) { list_del_init(&wk->entry); if (!list_empty(&rdev->wiphy_work_list)) - schedule_work(work); + queue_work(system_unbound_wq, work); spin_unlock_irq(&rdev->wiphy_work_lock); wk->func(&rdev->wiphy, wk); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index e106dcea3977..c569c37da317 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -56,7 +56,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.burst_period = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) out->ftm.burst_period = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); + nla_get_u16(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; if (out->ftm.asap && !capa->ftm.asap) { @@ -75,7 +75,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.num_bursts_exp = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) out->ftm.num_bursts_exp = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); if (capa->ftm.max_bursts_exponent >= 0 && out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) { @@ -88,7 +88,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.burst_duration = 15; if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) out->ftm.burst_duration = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); out->ftm.ftms_per_burst = 0; if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) @@ -107,7 +107,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, out->ftm.ftmr_retries = 3; if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) out->ftm.ftmr_retries = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; if (out->ftm.request_lci && !capa->ftm.request_lci) { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 43897a5269b6..755af47b88b9 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -2,7 +2,7 @@ /* * Portions of this file * Copyright(c) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018, 2021-2023 Intel Corporation + * Copyright (C) 2018, 2021-2024 Intel Corporation */ #ifndef __CFG80211_RDEV_OPS #define __CFG80211_RDEV_OPS @@ -458,6 +458,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev, struct cfg80211_scan_request *request) { int ret; + + if (WARN_ON_ONCE(!request->n_ssids && request->ssids)) + return -EINVAL; + trace_rdev_scan(&rdev->wiphy, request); ret = rdev->ops->scan(&rdev->wiphy, request); trace_rdev_return_int(&rdev->wiphy, ret); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 127853877a0a..2f2a3163968a 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -812,6 +812,7 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) LIST_HEAD(coloc_ap_list); bool need_scan_psc = true; const struct ieee80211_sband_iftype_data *iftd; + size_t size, offs_ssids, offs_6ghz_params, offs_ies; rdev_req->scan_6ghz = true; @@ -877,10 +878,15 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) spin_unlock_bh(&rdev->bss_lock); } - request = kzalloc(struct_size(request, channels, n_channels) + - sizeof(*request->scan_6ghz_params) * count + - sizeof(*request->ssids) * rdev_req->n_ssids, - GFP_KERNEL); + size = struct_size(request, channels, n_channels); + offs_ssids = size; + size += sizeof(*request->ssids) * rdev_req->n_ssids; + offs_6ghz_params = size; + size += sizeof(*request->scan_6ghz_params) * count; + offs_ies = size; + size += rdev_req->ie_len; + + request = kzalloc(size, GFP_KERNEL); if (!request) { cfg80211_free_coloc_ap_list(&coloc_ap_list); return -ENOMEM; @@ -888,8 +894,26 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) *request = *rdev_req; request->n_channels = 0; - request->scan_6ghz_params = - (void *)&request->channels[n_channels]; + request->n_6ghz_params = 0; + if (rdev_req->n_ssids) { + /* + * Add the ssids from the parent scan request to the new + * scan request, so the driver would be able to use them + * in its probe requests to discover hidden APs on PSC + * channels. + */ + request->ssids = (void *)request + offs_ssids; + memcpy(request->ssids, rdev_req->ssids, + sizeof(*request->ssids) * request->n_ssids); + } + request->scan_6ghz_params = (void *)request + offs_6ghz_params; + + if (rdev_req->ie_len) { + void *ie = (void *)request + offs_ies; + + memcpy(ie, rdev_req->ie, rdev_req->ie_len); + request->ie = ie; + } /* * PSC channels should not be scanned in case of direct scan with 1 SSID @@ -978,17 +1002,8 @@ skip: if (request->n_channels) { struct cfg80211_scan_request *old = rdev->int_scan_req; - rdev->int_scan_req = request; - /* - * Add the ssids from the parent scan request to the new scan - * request, so the driver would be able to use them in its - * probe requests to discover hidden APs on PSC channels. - */ - request->ssids = (void *)&request->channels[request->n_channels]; - request->n_ssids = rdev_req->n_ssids; - memcpy(request->ssids, rdev_req->ssids, sizeof(*request->ssids) * - request->n_ssids); + rdev->int_scan_req = request; /* * If this scan follows a previous scan, save the scan start @@ -2128,7 +2143,8 @@ static bool cfg80211_6ghz_power_type_valid(const u8 *ie, size_t ielen, struct ieee80211_he_operation *he_oper; tmp = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ie, ielen); - if (tmp && tmp->datalen >= sizeof(*he_oper) + 1) { + if (tmp && tmp->datalen >= sizeof(*he_oper) + 1 && + tmp->datalen >= ieee80211_he_oper_size(tmp->data + 1)) { const struct ieee80211_he_6ghz_oper *he_6ghz_oper; he_oper = (void *)&tmp->data[1]; diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 565511a3f461..62f26618f674 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -5,7 +5,7 @@ * * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz> * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2020-2021, 2023 Intel Corporation + * Copyright (C) 2020-2021, 2023-2024 Intel Corporation */ #include <linux/device.h> @@ -137,7 +137,7 @@ static int wiphy_resume(struct device *dev) if (rdev->wiphy.registered && rdev->ops->resume) ret = rdev_resume(rdev); rdev->suspended = false; - schedule_work(&rdev->wiphy_work); + queue_work(system_unbound_wq, &rdev->wiphy_work); wiphy_unlock(&rdev->wiphy); if (ret) diff --git a/net/wireless/util.c b/net/wireless/util.c index 2bde8a354631..082c6f9c5416 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2549,6 +2549,7 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; + int ret; wdev = dev->ieee80211_ptr; if (!wdev) @@ -2560,7 +2561,11 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, memset(sinfo, 0, sizeof(*sinfo)); - return rdev_get_station(rdev, dev, mac_addr, sinfo); + wiphy_lock(&rdev->wiphy); + ret = rdev_get_station(rdev, dev, mac_addr, sinfo); + wiphy_unlock(&rdev->wiphy); + + return ret; } EXPORT_SYMBOL(cfg80211_get_station); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 727aa20be4bd..7d1c0986f9bb 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -313,13 +313,10 @@ static bool xsk_is_bound(struct xdp_sock *xs) static int xsk_rcv_check(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) { - struct net_device *dev = xdp->rxq->dev; - u32 qid = xdp->rxq->queue_index; - if (!xsk_is_bound(xs)) return -ENXIO; - if (!dev->_rx[qid].pool || xs->umem != dev->_rx[qid].pool->umem) + if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index) return -EINVAL; if (len > xsk_pool_get_rx_frame_size(xs->pool) && !xs->sg) { diff --git a/scripts/atomic/kerneldoc/sub_and_test b/scripts/atomic/kerneldoc/sub_and_test index d3760f7749d4..96615e50836b 100644 --- a/scripts/atomic/kerneldoc/sub_and_test +++ b/scripts/atomic/kerneldoc/sub_and_test @@ -1,7 +1,7 @@ cat <<EOF /** * ${class}${atomicname}() - atomic subtract and test if zero with ${desc_order} ordering - * @i: ${int} value to add + * @i: ${int} value to subtract * @v: pointer to ${atomic}_t * * Atomically updates @v to (@v - @i) with ${desc_order} ordering. diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index 52e5bfb61fd0..e9589c372968 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -79,6 +79,7 @@ snd_rawmidi_ops snd_soc_component_driver snd_soc_dai_ops snd_soc_ops +snd_soc_tplg_ops soc_pcmcia_socket_ops stacktrace_ops sysfs_ops diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 387503daf0f7..85b53069ba7a 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -533,19 +533,6 @@ int conf_read(const char *name) */ if (sym->visible == no && !conf_unsaved) sym->flags &= ~SYMBOL_DEF_USER; - switch (sym->type) { - case S_STRING: - case S_INT: - case S_HEX: - /* Reset a string value if it's out of range */ - if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) - break; - sym->flags &= ~SYMBOL_VALID; - conf_unsaved++; - break; - default: - break; - } } } diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 4d95fce5f9a7..fcc190b67b6f 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -397,35 +397,6 @@ static struct expr *expr_eliminate_yn(struct expr *e) } /* - * bool FOO!=n => FOO - */ -struct expr *expr_trans_bool(struct expr *e) -{ - if (!e) - return NULL; - switch (e->type) { - case E_AND: - case E_OR: - case E_NOT: - e->left.expr = expr_trans_bool(e->left.expr); - e->right.expr = expr_trans_bool(e->right.expr); - break; - case E_UNEQUAL: - // FOO!=n -> FOO - if (e->left.sym->type == S_TRISTATE) { - if (e->right.sym == &symbol_no) { - e->type = E_SYMBOL; - e->right.sym = NULL; - } - } - break; - default: - ; - } - return e; -} - -/* * e1 || e2 -> ? */ static struct expr *expr_join_or(struct expr *e1, struct expr *e2) diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index fa50fc45622e..7c0c242318bc 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -284,7 +284,6 @@ void expr_free(struct expr *e); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); int expr_eq(struct expr *e1, struct expr *e2); tristate expr_calc_value(struct expr *e); -struct expr *expr_trans_bool(struct expr *e); struct expr *expr_eliminate_dups(struct expr *e); struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index cc400ffe6615..e04dbafd3add 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1422,7 +1422,6 @@ int main(int ac, char *av[]) conf_parse(name); fixup_rootmenu(&rootmenu); - conf_read(NULL); /* Load the interface and connect signals */ init_main_window(glade_file); @@ -1430,6 +1429,8 @@ int main(int ac, char *av[]) init_left_tree(); init_right_tree(); + conf_read(NULL); + switch (view_mode) { case SINGLE_VIEW: display_tree_part(); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 53151c5a6028..eef9b63cdf11 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -398,8 +398,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) dep = expr_transform(dep); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); - if (menu->sym && menu->sym->type != S_TRISTATE) - dep = expr_trans_bool(dep); prop->visible.expr = dep; /* diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 46ce5d04dbeb..518c70b8db50 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -193,7 +193,7 @@ kallsyms_step() mksysmap() { info NM ${2} - ${NM} -n "${1}" | "${srctree}/scripts/mksysmap" > "${2}" + ${NM} -n "${1}" | sed -f "${srctree}/scripts/mksysmap" > "${2}" } sorttable() diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 937294ff164f..f48d72d22dc2 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1647,10 +1647,11 @@ static void read_symbols(const char *modname) namespace = get_next_modinfo(&info, "import_ns", namespace); } + + if (extra_warn && !get_modinfo(&info, "description")) + warn("missing MODULE_DESCRIPTION() in %s\n", modname); } - if (extra_warn && !get_modinfo(&info, "description")) - warn("missing MODULE_DESCRIPTION() in %s\n", modname); for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = remove_dot(info.strtab + sym->st_name); diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index fad75be5f381..1e0dd1a6d0b0 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -10,7 +10,7 @@ config SECURITY_TOMOYO help This selects TOMOYO Linux, pathname-based access control. Required userspace tools and further information may be - found at <https://tomoyo.osdn.jp/>. + found at <https://tomoyo.sourceforge.net/>. If you are unsure how to answer this question, answer N. config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ea3140d510ec..5c7b059a332a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2787,7 +2787,7 @@ void tomoyo_check_profile(void) else continue; pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n"); - pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n"); + pr_err("Please see https://tomoyo.sourceforge.net/2.6/ for more information.\n"); panic("STOP!"); } tomoyo_read_unlock(idx); diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index d7417a40392b..f3b2a610df23 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -125,11 +125,11 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { struct sdw_intel_acpi_info *info = cdata; - acpi_status status; u64 adr; + int ret; - status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); - if (ACPI_FAILURE(status)) + ret = acpi_get_local_u64_address(handle, &adr); + if (ret < 0) return AE_OK; /* keep going */ if (!acpi_fetch_acpi_dev(handle)) { diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 879d4b48e6c7..49bd7097d892 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -830,7 +830,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) pm_runtime_set_active(tas_hda->dev); pm_runtime_enable(tas_hda->dev); - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops); if (ret) { @@ -925,7 +925,7 @@ static int tas2781_system_resume(struct device *dev) tas_hda->priv->tasdevice[i].cur_prog = -1; tas_hda->priv->tasdevice[i].cur_conf = -1; } - tas2781_reset(tas_hda->priv); + tasdevice_reset(tas_hda->priv); tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog); /* If calibrated data occurs error, dsp will still work with default diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c index e079b3218c6f..3756b8bef17b 100644 --- a/sound/soc/amd/acp-es8336.c +++ b/sound/soc/amd/acp-es8336.c @@ -203,8 +203,10 @@ static int st_es8336_late_probe(struct snd_soc_card *card) codec_dev = acpi_get_first_physical_node(adev); acpi_dev_put(adev); - if (!codec_dev) + if (!codec_dev) { dev_err(card->dev, "can not find codec dev\n"); + return -ENODEV; + } ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios); if (ret) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index ef12f97ddc69..97258b4cf89b 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -369,12 +369,12 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct } writel(period_bytes, adata->acp_base + water_val); writel(buf_size, adata->acp_base + buf_reg); + if (rsrc->soc_mclk) + acp_set_i2s_clk(adata, dai->driver->id); val = readl(adata->acp_base + reg_val); val = val | BIT(0); writel(val, adata->acp_base + reg_val); writel(1, adata->acp_base + ier_val); - if (rsrc->soc_mclk) - acp_set_i2s_clk(adata, dai->driver->id); return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -584,21 +584,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } -static int acp_i2s_probe(struct snd_soc_dai *dai) -{ - struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - - if (!adata->acp_base) { - dev_err(dev, "I2S base is NULL\n"); - return -EINVAL; - } - - return 0; -} - const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .probe = acp_i2s_probe, .startup = acp_i2s_startup, .hw_params = acp_i2s_hwparams, .prepare = acp_i2s_prepare, @@ -608,5 +594,6 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { }; EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP Audio I2S controller"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index 3be7c6d55a6f..4422cec81e3c 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -475,4 +475,5 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) } EXPORT_SYMBOL_NS_GPL(check_acp_config, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP legacy common features"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 777b5a78d8a9..b0304b813cad 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -249,6 +249,7 @@ static struct pci_driver snd_amd_acp_pci_driver = { }; module_pci_driver(snd_amd_acp_pci_driver); +MODULE_DESCRIPTION("AMD ACP common PCI support"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index f754bf79b5e3..bb79269c2fc1 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -178,5 +178,6 @@ const struct snd_soc_dai_ops acp_dmic_dai_ops = { }; EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, SND_SOC_ACP_COMMON); +MODULE_DESCRIPTION("AMD ACP Audio PDM controller"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index aaac8aa744cb..4f409cd09c11 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -197,6 +197,20 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs else runtime->hw = acp_pcm_hardware_capture; + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n"); + kfree(stream); + return ret; + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n"); + kfree(stream); + return ret; + } + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(component->dev, "set integer constraint failed\n"); diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 158f819f8da4..e19981c7d65a 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -39,8 +39,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; @@ -231,12 +229,13 @@ static int rembrandt_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = REMBRANDT; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_rmb_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp6x_master_clock_generate(dev); if (ret) return ret; @@ -269,7 +268,7 @@ static int __maybe_unused rmb_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp6x_master_clock_generate(dev); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index b0e181c9a733..db835ed7c208 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -32,8 +32,6 @@ static struct acp_resource rsrc = { .no_of_ctrls = 1, .irqp_used = 0, .irq_reg_offset = 0x1800, - .i2s_pin_cfg_offset = 0x1400, - .i2s_mode = 0x04, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x02052800, }; diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index 4d342441a650..f340920b3289 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -55,8 +55,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; @@ -241,11 +239,12 @@ static int acp63_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = ACP63; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_acp63_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp63_i2s_master_clock_generate(adata); if (ret) return ret; @@ -278,7 +277,7 @@ static int __maybe_unused acp63_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp63_i2s_master_clock_generate(adata); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index 0d7cdd4017e5..a2cbdcca4313 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -31,8 +31,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index d75b4eb34de8..87a4813783f9 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -162,8 +162,6 @@ struct acp_resource { int irqp_used; bool soc_mclk; u32 irq_reg_offset; - u32 i2s_pin_cfg_offset; - int i2s_mode; u64 scratch_reg_offset; u64 sram_pte_offset; }; @@ -175,6 +173,7 @@ struct acp_dev_data { unsigned int i2s_irq; bool tdm_mode; + bool is_i2s_config; /* SOC specific dais */ struct snd_soc_dai_driver *dai_driver; int num_dai; diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c index e675b8f569eb..ff8ad036b077 100644 --- a/sound/soc/amd/ps/ps-mach.c +++ b/sound/soc/amd/ps/ps-mach.c @@ -75,5 +75,6 @@ static struct platform_driver acp63_mach_driver = { module_platform_driver(acp63_mach_driver); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); +MODULE_DESCRIPTION("AMD Pink Sardine support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/renoir/acp3x-rn.c b/sound/soc/amd/renoir/acp3x-rn.c index 5d979a7b77fb..3249f74a0197 100644 --- a/sound/soc/amd/renoir/acp3x-rn.c +++ b/sound/soc/amd/renoir/acp3x-rn.c @@ -72,5 +72,6 @@ static struct platform_driver acp_mach_driver = { module_platform_driver(acp_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD Renoir support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 4e3a8ce690a4..f54466ed8e3e 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -511,5 +511,6 @@ static struct platform_driver acp6x_mach_driver = { module_platform_driver(acp6x_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD Yellow Carp support for DMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4afc43d3f71f..97bb69c9848d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -45,6 +45,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK4535 imply SND_SOC_AK4554 imply SND_SOC_AK4613 + imply SND_SOC_AK4619 imply SND_SOC_AK4641 imply SND_SOC_AK4642 imply SND_SOC_AK4671 @@ -99,6 +100,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS47L90 imply SND_SOC_CS47L92 imply SND_SOC_CS53L30 + imply SND_SOC_CS530X_I2C imply SND_SOC_CX20442 imply SND_SOC_CX2072X imply SND_SOC_DA7210 @@ -221,7 +223,9 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318 imply SND_SOC_RT1318_SDW + imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 imply SND_SOC_RTQ9128 imply SND_SOC_SDW_MOCKUP @@ -278,6 +282,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X + imply SND_SOC_WCD937X_SDW imply SND_SOC_WCD938X_SDW imply SND_SOC_WCD939X_SDW imply SND_SOC_LPASS_MACRO_COMMON @@ -596,6 +601,10 @@ config SND_SOC_AK4613 tristate "AKM AK4613 CODEC" depends on I2C +config SND_SOC_AK4619 + tristate "AKM AK4619 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate depends on I2C @@ -997,6 +1006,19 @@ config SND_SOC_CS53L30 tristate "Cirrus Logic CS53L30 CODEC" depends on I2C +config SND_SOC_CS530X + tristate + +config SND_SOC_CS530X_I2C + tristate "Cirrus Logic CS530x ADCs (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS530X + help + Enable support for Cirrus Logic CS530X ADCs + with I2C control. + config SND_SOC_CX20442 tristate depends on TTY @@ -1101,6 +1123,10 @@ config SND_SOC_ES83XX_DSM_COMMON depends on ACPI tristate +config SND_SOC_ES8311 + tristate "Everest Semi ES8311 CODEC" + depends on I2C + config SND_SOC_ES8316 tristate "Everest Semi ES8316 CODEC" depends on I2C @@ -1570,11 +1596,21 @@ config SND_SOC_RT1316_SDW depends on SOUNDWIRE select REGMAP_SOUNDWIRE +config SND_SOC_RT1318 + tristate + depends on I2C + config SND_SOC_RT1318_SDW tristate "Realtek RT1318 Codec - SDW" depends on SOUNDWIRE select REGMAP_SOUNDWIRE +config SND_SOC_RT1320_SDW + tristate "Realtek RT1320 Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT5514 tristate depends on I2C @@ -2100,6 +2136,25 @@ config SND_SOC_WCD934X The WCD9340/9341 is a audio codec IC Integrated in Qualcomm SoCs like SDM845. +config SND_SOC_WCD937X + depends on SND_SOC_WCD937X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + select SND_SOC_WCD_CLASSH + +config SND_SOC_WCD937X_SDW + tristate "WCD9370/WCD9375 Codec - SDW" + select SND_SOC_WCD937X + select SND_SOC_WCD_MBHC + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9370/9375 is an audio codec IC used with SoCs + like SC7280 or QCM6490 chipsets, and it connected + via soundwire. + To compile this codec driver say Y or m. + config SND_SOC_WCD938X depends on SND_SOC_WCD938X_SDW tristate @@ -2502,6 +2557,7 @@ config SND_SOC_LPASS_MACRO_COMMON config SND_SOC_LPASS_WSA_MACRO depends on COMMON_CLK select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" config SND_SOC_LPASS_VA_MACRO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b4df22186e25..5868007e1045 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -39,6 +39,7 @@ snd-soc-ak4458-y := ak4458.o snd-soc-ak4535-y := ak4535.o snd-soc-ak4554-y := ak4554.o snd-soc-ak4613-y := ak4613.o +snd-soc-ak4619-y := ak4619.o snd-soc-ak4641-y := ak4641.o snd-soc-ak4642-y := ak4642.o snd-soc-ak4671-y := ak4671.o @@ -107,6 +108,8 @@ snd-soc-cs47l85-y := cs47l85.o snd-soc-cs47l90-y := cs47l90.o snd-soc-cs47l92-y := cs47l92.o snd-soc-cs53l30-y := cs53l30.o +snd-soc-cs530x-y := cs530x.o +snd-soc-cs530x-i2c-y := cs530x-i2c.o snd-soc-cx20442-y := cx20442.o snd-soc-cx2072x-y := cx2072x.o snd-soc-da7210-y := da7210.o @@ -119,6 +122,7 @@ snd-soc-dmic-y := dmic.o snd-soc-es7134-y := es7134.o snd-soc-es7241-y := es7241.o snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o +snd-soc-es8311-y := es8311.o snd-soc-es8316-y := es8316.o snd-soc-es8326-y := es8326.o snd-soc-es8328-y := es8328.o @@ -221,7 +225,9 @@ snd-soc-rt1305-y := rt1305.o snd-soc-rt1308-y := rt1308.o snd-soc-rt1308-sdw-y := rt1308-sdw.o snd-soc-rt1316-sdw-y := rt1316-sdw.o +snd-soc-rt1318-y := rt1318.o snd-soc-rt1318-sdw-y := rt1318-sdw.o +snd-soc-rt1320-sdw-y := rt1320-sdw.o snd-soc-rt274-y := rt274.o snd-soc-rt286-y := rt286.o snd-soc-rt298-y := rt298.o @@ -316,6 +322,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o snd-soc-wcd9335-y := wcd9335.o snd-soc-wcd934x-y := wcd934x.o +snd-soc-wcd937x-objs := wcd937x.o +snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o snd-soc-wcd938x-y := wcd938x.o snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o @@ -435,6 +443,7 @@ obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o +obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o @@ -504,6 +513,8 @@ obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o +obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o +obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o @@ -516,6 +527,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o +obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o @@ -613,7 +625,9 @@ obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o +obj-$(CONFIG_SND_SOC_RT1318) += snd-soc-rt1318.o obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o +obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o @@ -710,6 +724,11 @@ obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o +obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x.o +ifdef CONFIG_SND_SOC_WCD937X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o +endif obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o ifdef CONFIG_SND_SOC_WCD938X_SDW # avoid link failure by forcing sdw code built-in when needed diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c index a663d37e5776..abc4764697a5 100644 --- a/sound/soc/codecs/adau7118.c +++ b/sound/soc/codecs/adau7118.c @@ -121,8 +121,10 @@ static const struct snd_soc_dapm_widget adau7118_widgets[] = { }; static int adau7118_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct adau7118_data *st = snd_soc_component_get_drvdata(dai->component); diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 9a43235e6a11..23e868e4e3fb 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -9,7 +9,6 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 73cf482f104f..d472d9952628 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -10,7 +10,6 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -46,7 +45,6 @@ struct ak4458_priv { const struct ak4458_drvdata *drvdata; struct device *dev; struct regmap *regmap; - struct gpio_desc *reset_gpiod; struct reset_control *reset; struct gpio_desc *mute_gpiod; int digfil; /* SSLOW, SD, SLOW bits */ @@ -632,10 +630,7 @@ static struct snd_soc_dai_driver ak4497_dai = { static void ak4458_reset(struct ak4458_priv *ak4458, bool active) { - if (ak4458->reset_gpiod) { - gpiod_set_value_cansleep(ak4458->reset_gpiod, active); - usleep_range(1000, 2000); - } else if (!IS_ERR_OR_NULL(ak4458->reset)) { + if (!IS_ERR_OR_NULL(ak4458->reset)) { if (active) reset_control_assert(ak4458->reset); else @@ -759,11 +754,6 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->reset)) return PTR_ERR(ak4458->reset); - ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ak4458->reset_gpiod)) - return PTR_ERR(ak4458->reset_gpiod); - ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute", GPIOD_OUT_LOW); if (IS_ERR(ak4458->mute_gpiod)) diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 73fb35560e51..551738abd1a5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -753,7 +753,7 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 ak4613_dai_formats = +static const u64 ak4613_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J; diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c new file mode 100644 index 000000000000..8f2442482f72 --- /dev/null +++ b/sound/soc/codecs/ak4619.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4619.c -- Asahi Kasei ALSA SoC Audio driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + * Khanh Le <khanh.le.xr@renesas.com> + * + * Based on ak4613.c by Kuninori Morimoto + * Based on da7213.c by Adam Thomson + * Based on ak4641.c by Harald Welte + */ + +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +/* + * Registers + */ + +#define PWR_MGMT 0x00 /* Power Management */ +#define AU_IFF1 0x01 /* Audio I/F Format */ +#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */ +#define SYS_CLK 0x03 /* System Clock Setting */ +#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */ +#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */ +#define LADC1 0x06 /* ADC1 Lch Digital Volume */ +#define RADC1 0x07 /* ADC1 Rch Digital Volume */ +#define LADC2 0x08 /* ADC2 Lch Digital Volume */ +#define RADC2 0x09 /* ADC2 Rch Digital Volume */ +#define ADC_DF 0x0a /* ADC Digital Filter Setting */ +#define ADC_AI 0x0b /* ADC Analog Input Setting */ +#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */ +#define LDAC1 0x0E /* DAC1 Lch Digital Volume */ +#define RDAC1 0x0F /* DAC1 Rch Digital Volume */ +#define LDAC2 0x10 /* DAC2 Lch Digital Volume */ +#define RDAC2 0x11 /* DAC2 Rch Digital Volume */ +#define DAC_IS 0x12 /* DAC Input Select Setting */ +#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */ +#define DAC_MF 0x14 /* DAC Mute & Filter Setting */ + +/* + * Bit fields + */ + +/* Power Management */ +#define PMAD2 BIT(5) +#define PMAD1 BIT(4) +#define PMDA2 BIT(2) +#define PMDA1 BIT(1) +#define RSTN BIT(0) + +/* Audio_I/F Format */ +#define DCF_STEREO_I2S (0x0 << 4) +#define DCF_STEREO_MSB (0x5 << 4) +#define DCF_PCM_SF (0x6 << 4) +#define DCF_PCM_LF (0x7 << 4) +#define DSL_32 (0x3 << 2) +#define DCF_MASK (0x7 << 4) +#define DSL_MASK (0x3 << 2) +#define BCKP BIT(1) + +/* Audio_I/F Format (Extended) */ +#define DIDL_24 (0x0 << 2) +#define DIDL_20 (0x1 << 2) +#define DIDL_16 (0x2 << 2) +#define DIDL_32 (0x3 << 2) +#define DODL_24 (0x0 << 0) +#define DODL_20 (0x1 << 0) +#define DODL_16 (0x2 << 0) +#define DIDL_MASK (0x3 << 2) +#define DODL_MASK (0x3 << 0) +#define SLOT BIT(4) + +/* System Clock Setting */ +#define FS_MASK 0x7 + +/* MIC AMP Gain */ +#define MGNL_SHIFT 4 +#define MGNR_SHIFT 0 +#define MGN_MAX 0xB + +/* ADC Digital Volume */ +#define VOLAD_SHIFT 0 +#define VOLAD_MAX 0xFF + +/* ADC Digital Filter Setting */ +#define AD1SL_SHIFT 0 +#define AD2SL_SHIFT 4 + +/* Analog Input Select */ +#define AD1LSEL_SHIFT 6 +#define AD1RSEL_SHIFT 4 +#define AD2LSEL_SHIFT 2 +#define AD2RSEL_SHIFT 0 + +/* ADC Mute & HPF Control */ +#define ATSPAD_SHIFT 7 +#define AD1MUTE_SHIFT 5 +#define AD2MUTE_SHIFT 6 +#define AD1MUTE_MAX 1 +#define AD2MUTE_MAX 1 +#define AD1MUTE_EN BIT(5) +#define AD2MUTE_EN BIT(6) +#define AD1HPFN_SHIFT 1 +#define AD1HPFN_MAX 1 +#define AD2HPFN_SHIFT 2 +#define AD2HPFN_MAX 1 + +/* DAC Digital Volume */ +#define VOLDA_SHIFT 0 +#define VOLDA_MAX 0xFF + +/* DAC Input Select Setting */ +#define DAC1SEL_SHIFT 0 +#define DAC2SEL_SHIFT 2 + +/* DAC De-Emphasis Setting */ +#define DEM1_32000 (0x3 << 0) +#define DEM1_44100 (0x0 << 0) +#define DEM1_48000 (0x2 << 0) +#define DEM1_OFF (0x1 << 0) +#define DEM2_32000 (0x3 << 2) +#define DEM2_44100 (0x0 << 2) +#define DEM2_48000 (0x2 << 2) +#define DEM2_OFF (0x1 << 2) +#define DEM1_MASK (0x3 << 0) +#define DEM2_MASK (0x3 << 2) +#define DEM1_SHIFT 0 +#define DEM2_SHIFT 2 + +/* DAC Mute & Filter Setting */ +#define DA1MUTE_SHIFT 4 +#define DA1MUTE_MAX 1 +#define DA2MUTE_SHIFT 5 +#define DA2MUTE_MAX 1 +#define DA1MUTE_EN BIT(4) +#define DA2MUTE_EN BIT(5) +#define ATSPDA_SHIFT 7 +#define DA1SL_SHIFT 0 +#define DA2SL_SHIFT 2 + +/* Codec private data */ +struct ak4619_priv { + struct regmap *regmap; + struct snd_pcm_hw_constraint_list constraint; + int deemph_en; + unsigned int playback_rate; + unsigned int sysclk; +}; + +/* + * DAC Volume + * + * max : 0x00 : +12.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -115.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1); + +/* + * MIC Volume + * + * max : 0x0B : +27.0 dB + * ( 3 dB step ) + * min: 0x00 : -6.0 dB + */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0); + +/* + * ADC Volume + * + * max : 0x00 : +24.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -103.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); + +/* ADC & DAC Volume Level Transition Time select */ +static const char * const ak4619_vol_trans_txt[] = { + "4/fs", "16/fs" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt); + +/* ADC Digital Filter select */ +static const char * const ak4619_adc_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter", + "Voice Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt); + +/* DAC De-Emphasis Filter select */ +static const char * const ak4619_dac_de_emp_txt[] = { + "44.1kHz", "OFF", "48kHz", "32kHz" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt); + +/* DAC Digital Filter select */ +static const char * const ak4619_dac_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt); + +/* + * Control functions + */ + +static void ak4619_set_deemph(struct snd_soc_component *component) +{ + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + u8 dem = 0; + + if (!ak4619->deemph_en) + return; + + switch (ak4619->playback_rate) { + case 32000: + dem |= DEM1_32000 | DEM2_32000; + break; + case 44100: + dem |= DEM1_44100 | DEM2_44100; + break; + case 48000: + dem |= DEM1_48000 | DEM2_48000; + break; + default: + dem |= DEM1_OFF | DEM2_OFF; + break; + } + snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem); +} + +static int ak4619_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + int deemph_en = ucontrol->value.integer.value[0]; + int ret = 0; + + switch (deemph_en) { + case 0: + case 1: + break; + default: + return -EINVAL; + } + + if (ak4619->deemph_en != deemph_en) + ret = 1; /* The value changed */ + + ak4619->deemph_en = deemph_en; + ak4619_set_deemph(component); + + return ret; +} + +static int ak4619_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = ak4619->deemph_en; + + return 0; +}; + +/* + * KControls + */ +static const struct snd_kcontrol_new ak4619_snd_controls[] = { + + /* Volume controls */ + SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + + SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + + /* Volume Level Transition Time controls */ + SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans), + SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans), + + /* Mute controls */ + SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1), + SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1), + + SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1), + SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1), + + /* Filter controls */ + SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil), + SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil), + + SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1), + SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1), + + SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp), + SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp), + + SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil), + SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil), + + SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph), +}; + +/* + * DAPM + */ + +/* Analog input mode */ +static const char * const ak4619_analog_in_txt[] = { + "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt); + +static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in); +static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in); +static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in); +static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in); + +/* DAC source mux */ +static const char * const ak4619_dac_in_txt[] = { + "SDIN1", "SDIN2", "SDOUT1", "SDOUT2" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt); + +static const struct snd_kcontrol_new ak4619_dac_1_in_mux = + SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in); +static const struct snd_kcontrol_new ak4619_dac_2_in_mux = + SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in); + +static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = { + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN5L"), + + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN4R"), + SND_SOC_DAPM_INPUT("AIN5R"), + + SND_SOC_DAPM_INPUT("MIC1L"), + SND_SOC_DAPM_INPUT("MIC1R"), + SND_SOC_DAPM_INPUT("MIC2L"), + SND_SOC_DAPM_INPUT("MIC2R"), + + /* DAI */ + SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux), + + /* MUXs for DAC source selection */ + SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux), + SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux), +}; + +static const struct snd_soc_dapm_route ak4619_intercon[] = { + /* Dest Connecting Widget Source */ + + /* Output path */ + {"AOUT1L", NULL, "DAC1"}, + {"AOUT2L", NULL, "DAC2"}, + + {"AOUT1R", NULL, "DAC1"}, + {"AOUT2R", NULL, "DAC2"}, + + {"DAC1", NULL, "DAC 1 Source MUX"}, + {"DAC2", NULL, "DAC 2 Source MUX"}, + + {"DAC 1 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 1 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"}, + + {"DAC 2 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 2 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"}, + + /* Input path */ + {"SDOUT1", NULL, "ADC1"}, + {"SDOUT2", NULL, "ADC2"}, + + {"ADC1", NULL, "Analog Input 1 Left MUX"}, + {"ADC1", NULL, "Analog Input 1 Right MUX"}, + + {"ADC2", NULL, "Analog Input 2 Left MUX"}, + {"ADC2", NULL, "Analog Input 2 Right MUX"}, + + {"Analog Input 1 Left MUX", "Differential", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"}, + {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"}, + + {"Analog Input 1 Right MUX", "Differential", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"}, + {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"}, + + {"Analog Input 2 Left MUX", "Differential", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"}, + {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"}, + + {"Analog Input 2 Right MUX", "Differential", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"}, + {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"}, + + {"MIC1L", NULL, "AIN1L"}, + {"MIC1L", NULL, "AIN2L"}, + + {"MIC1R", NULL, "AIN1R"}, + {"MIC1R", NULL, "AIN2R"}, + + {"MIC2L", NULL, "AIN4L"}, + {"MIC2L", NULL, "AIN5L"}, + + {"MIC2R", NULL, "AIN4R"}, + {"MIC2R", NULL, "AIN5R"}, +}; + +static const struct reg_default ak4619_reg_defaults[] = { + { PWR_MGMT, 0x00 }, + { AU_IFF1, 0x0C }, + { AU_IFF2, 0x0C }, + { SYS_CLK, 0x00 }, + { MIC_AMP1, 0x22 }, + { MIC_AMP2, 0x22 }, + { LADC1, 0x30 }, + { RADC1, 0x30 }, + { LADC2, 0x30 }, + { RADC2, 0x30 }, + { ADC_DF, 0x00 }, + { ADC_AI, 0x00 }, + { ADC_MHPF, 0x00 }, + { LDAC1, 0x18 }, + { RDAC1, 0x18 }, + { LDAC2, 0x18 }, + { RDAC2, 0x18 }, + { DAC_IS, 0x04 }, + { DAC_DEMP, 0x05 }, + { DAC_MF, 0x0A }, +}; + +static int ak4619_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u8 pwr_ctrl = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + pwr_ctrl |= RSTN; + fallthrough; + case SND_SOC_BIAS_PREPARE: + pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2; + fallthrough; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_component_write(component, PWR_MGMT, pwr_ctrl); + + return 0; +} + +static int ak4619_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + unsigned int width; + unsigned int rate; + unsigned int fs; + bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 dai_ctrl = 0; + u8 clk_mode = 0; + + width = params_width(params); + switch (width) { + case 16: + dai_ctrl |= is_play ? DIDL_16 : DODL_16; + break; + case 20: + dai_ctrl |= is_play ? DIDL_20 : DODL_20; + break; + case 24: + dai_ctrl |= is_play ? DIDL_24 : DODL_24; + break; + case 32: + if (is_play) + dai_ctrl |= DIDL_32; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + rate = params_rate(params); + if (rate) + fs = ak4619->sysclk / rate; + else + return -EINVAL; + + switch (rate) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + switch (fs) { + case 256: + clk_mode |= (0x0 << 0); + break; + case 384: + clk_mode |= (0x2 << 0); + break; + case 512: + clk_mode |= (0x3 << 0); + break; + default: + return -EINVAL; + } + break; + case 64000: + case 88200: + case 96000: + if (fs == 256) + clk_mode |= (0x1 << 0); + else + return -EINVAL; + break; + case 176400: + case 192000: + if (fs == 128) + clk_mode |= (0x4 << 0); + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode); + snd_soc_component_update_bits(component, AU_IFF2, + is_play ? DIDL_MASK : DODL_MASK, dai_ctrl); + + if (is_play) { + ak4619->playback_rate = rate; + ak4619_set_deemph(component); + } + + return 0; +} + +static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 dai_fmt1 = 0; + u8 dai_fmt2 = 0; + + /* Set clock normal/inverted */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + dai_fmt1 |= BCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + default: + return -EINVAL; + } + + /* Only Stereo modes are supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_fmt1 |= DCF_STEREO_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_fmt1 |= DCF_STEREO_MSB; + break; + case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ + dai_fmt1 |= DCF_PCM_SF; + dai_fmt2 |= SLOT; + break; + case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ + dai_fmt1 |= DCF_PCM_LF; + dai_fmt2 |= SLOT; + break; + default: + return -EINVAL; + } + + /* Only slave mode is support */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* By default only 64 BICK per LRCLK is supported */ + dai_fmt1 |= DSL_32; + + snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK | + DSL_MASK | BCKP, dai_fmt1); + snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2); + + return 0; +} + +static int ak4619_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619->sysclk = freq; + + return 0; +} + +static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0); + snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0); + + return 0; +} + +static void ak4619_hw_constraints(struct ak4619_priv *ak4619, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint; + int ak4619_rate_mask = 0; + unsigned int fs; + int i; + static const unsigned int ak4619_sr[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, + }; + + /* + * [8kHz - 48kHz] : 256fs, 384fs or 512fs + * [64kHz - 96kHz] : 256fs + * [176.4kHz, 192kHz] : 128fs + */ + + for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) { + fs = ak4619->sysclk / ak4619_sr[i]; + + switch (fs) { + case 512: + case 384: + case 256: + ak4619_rate_mask |= (1 << i); + break; + case 128: + switch (i) { + case (ARRAY_SIZE(ak4619_sr) - 1): + case (ARRAY_SIZE(ak4619_sr) - 2): + ak4619_rate_mask |= (1 << i); + break; + default: + break; + } + break; + default: + break; + } + } + + constraint->list = ak4619_sr; + constraint->mask = ak4619_rate_mask; + constraint->count = ARRAY_SIZE(ak4619_sr); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint); +}; + +#define PLAYBACK_MODE 0 +#define CAPTURE_MODE 1 + +static int ak4619_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619_hw_constraints(ak4619, substream->runtime); + + return 0; +} + +static u64 ak4619_dai_formats[] = { + /* + * Select below from Sound Card, not here + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ + + /* First Priority */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J, + + /* Second Priority */ + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops ak4619_dai_ops = { + .startup = ak4619_dai_startup, + .set_sysclk = ak4619_dai_set_sysclk, + .set_fmt = ak4619_dai_set_fmt, + .hw_params = ak4619_dai_hw_params, + .mute_stream = ak4619_dai_mute, + .auto_selectable_formats = ak4619_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats), +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4619 = { + .set_bias_level = ak4619_set_bias_level, + .controls = ak4619_snd_controls, + .num_controls = ARRAY_SIZE(ak4619_snd_controls), + .dapm_widgets = ak4619_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets), + .dapm_routes = ak4619_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4619_intercon), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4619_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x14, + .reg_defaults = ak4619_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static const struct of_device_id ak4619_of_match[] = { + { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4619_of_match); + +static const struct i2c_device_id ak4619_i2c_id[] = { + { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id); + +#define AK4619_RATES SNDRV_PCM_RATE_8000_192000 + +#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4619_dai = { + .name = "ak4619-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_DAC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_ADC_FORMATS, + }, + .ops = &ak4619_dai_ops, + .symmetric_rate = 1, +}; + +static int ak4619_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct ak4619_priv *ak4619; + int ret; + + ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL); + if (!ak4619) + return -ENOMEM; + + i2c_set_clientdata(i2c, ak4619); + + ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg); + if (IS_ERR(ak4619->regmap)) { + ret = PTR_ERR(ak4619->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619, + &ak4619_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register ak4619 component: %d\n", + ret); + return ret; + } + + return 0; +} + +static struct i2c_driver ak4619_i2c_driver = { + .driver = { + .name = "ak4619-codec", + .of_match_table = ak4619_of_match, + }, + .probe = ak4619_i2c_probe, + .id_table = ak4619_i2c_id, +}; +module_i2c_driver(ak4619_i2c_driver); + +MODULE_DESCRIPTION("SoC AK4619 driver"); +MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 1e8e1effc2af..588e48044c13 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -6,6 +6,7 @@ // // Author: Herve Codina <herve.codina@bootlin.com> +#include <linux/cleanup.h> #include <linux/iio/consumer.h> #include <linux/minmax.h> #include <linux/mod_devicetable.h> @@ -131,33 +132,27 @@ static int audio_iio_aux_add_dapms(struct snd_soc_component *component, struct audio_iio_aux_chan *chan) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - char *output_name; - char *input_name; - char *pga_name; int ret; - input_name = kasprintf(GFP_KERNEL, "%s IN", chan->name); + /* Allocated names are not needed afterwards (duplicated in ASoC internals) */ + char *input_name __free(kfree) = kasprintf(GFP_KERNEL, "%s IN", chan->name); if (!input_name) return -ENOMEM; - output_name = kasprintf(GFP_KERNEL, "%s OUT", chan->name); - if (!output_name) { - ret = -ENOMEM; - goto out_free_input_name; - } + char *output_name __free(kfree) = kasprintf(GFP_KERNEL, "%s OUT", chan->name); + if (!output_name) + return -ENOMEM; - pga_name = kasprintf(GFP_KERNEL, "%s PGA", chan->name); - if (!pga_name) { - ret = -ENOMEM; - goto out_free_output_name; - } + char *pga_name __free(kfree) = kasprintf(GFP_KERNEL, "%s PGA", chan->name); + if (!pga_name) + return -ENOMEM; widgets[0] = SND_SOC_DAPM_INPUT(input_name); widgets[1] = SND_SOC_DAPM_OUTPUT(output_name); widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0); ret = snd_soc_dapm_new_controls(dapm, widgets, 3); if (ret) - goto out_free_pga_name; + return ret; routes[0].sink = pga_name; routes[0].control = NULL; @@ -165,17 +160,8 @@ static int audio_iio_aux_add_dapms(struct snd_soc_component *component, routes[1].sink = output_name; routes[1].control = NULL; routes[1].source = pga_name; - ret = snd_soc_dapm_add_routes(dapm, routes, 2); - - /* Allocated names are no more needed (duplicated in ASoC internals) */ -out_free_pga_name: - kfree(pga_name); -out_free_output_name: - kfree(output_name); -out_free_input_name: - kfree(input_name); - return ret; + return snd_soc_dapm_add_routes(dapm, routes, 2); } static int audio_iio_aux_component_probe(struct snd_soc_component *component) @@ -244,8 +230,6 @@ static int audio_iio_aux_probe(struct platform_device *pdev) struct audio_iio_aux_chan *iio_aux_chan; struct device *dev = &pdev->dev; struct audio_iio_aux *iio_aux; - const char **names; - u32 *invert_ranges; int count; int ret; int i; @@ -262,22 +246,22 @@ static int audio_iio_aux_probe(struct platform_device *pdev) iio_aux->num_chans = count; - names = kcalloc(iio_aux->num_chans, sizeof(*names), GFP_KERNEL); + const char **names __free(kfree) = kcalloc(iio_aux->num_chans, + sizeof(*names), + GFP_KERNEL); if (!names) return -ENOMEM; - invert_ranges = kcalloc(iio_aux->num_chans, sizeof(*invert_ranges), GFP_KERNEL); - if (!invert_ranges) { - ret = -ENOMEM; - goto out_free_names; - } + u32 *invert_ranges __free(kfree) = kcalloc(iio_aux->num_chans, + sizeof(*invert_ranges), + GFP_KERNEL); + if (!invert_ranges) + return -ENOMEM; ret = device_property_read_string_array(dev, "io-channel-names", names, iio_aux->num_chans); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to read io-channel-names\n"); - goto out_free_invert_ranges; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read io-channel-names\n"); /* * snd-control-invert-range is optional and can contain fewer items @@ -288,10 +272,8 @@ static int audio_iio_aux_probe(struct platform_device *pdev) count = min_t(unsigned int, count, iio_aux->num_chans); ret = device_property_read_u32_array(dev, "snd-control-invert-range", invert_ranges, count); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); - goto out_free_invert_ranges; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); } for (i = 0; i < iio_aux->num_chans; i++) { @@ -300,23 +282,16 @@ static int audio_iio_aux_probe(struct platform_device *pdev) iio_aux_chan->is_invert_range = invert_ranges[i]; iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name); - if (IS_ERR(iio_aux_chan->iio_chan)) { - ret = PTR_ERR(iio_aux_chan->iio_chan); - dev_err_probe(dev, ret, "get IIO channel '%s' failed\n", - iio_aux_chan->name); - goto out_free_invert_ranges; - } + if (IS_ERR(iio_aux_chan->iio_chan)) + return dev_err_probe(dev, PTR_ERR(iio_aux_chan->iio_chan), + "get IIO channel '%s' failed\n", + iio_aux_chan->name); } platform_set_drvdata(pdev, iio_aux); - ret = devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, - NULL, 0); -out_free_invert_ranges: - kfree(invert_ranges); -out_free_names: - kfree(names); - return ret; + return devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, + NULL, 0); } static const struct of_device_id audio_iio_aux_ids[] = { diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 79521ff44001..110009616966 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -445,7 +445,7 @@ static int aw87390_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw87390_i2c_id[] = { - { AW87390_I2C_NAME, 0 }, + { AW87390_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a78ceedd0334..fb99871578c5 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -1266,7 +1266,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88261_i2c_id[] = { - { AW88261_I2C_NAME, 0 }, + { AW88261_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index 3c459a67ad0c..aea44a199b98 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -8,9 +8,9 @@ // Author: Weidong Wang <wangweidong.a@awinic.com> // +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/firmware.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <sound/soc.h> #include "aw88395.h" @@ -560,7 +560,7 @@ static int aw88395_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88395_i2c_id[] = { - { AW88395_I2C_NAME, 0 }, + { AW88395_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index f25f6e0d4428..769ca32a5c8e 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -7,6 +7,7 @@ // Author: Bruce zhao <zhaolei@awinic.com> // +#include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/i2c.h> #include "aw88395_lib.h" @@ -361,11 +362,11 @@ static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { - struct aw_bin *aw_bin; int ret; int i; - aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(struct aw_bin), + GFP_KERNEL); if (!aw_bin) return -ENOMEM; @@ -375,7 +376,7 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->dev, "parse bin failed"); - goto parse_bin_failed; + return ret; } for (i = 0; i < aw_bin->all_bin_parse_num; i++) { @@ -387,10 +388,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * data + aw_bin->header_info[i].valid_data_addr; break; case DATA_TYPE_DSP_REG: - if (aw_bin->header_info[i].valid_data_len & 0x01) { - ret = -EINVAL; - goto parse_bin_failed; - } + if (aw_bin->header_info[i].valid_data_len & 0x01) + return -EINVAL; swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); @@ -402,10 +401,8 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * break; case DATA_TYPE_DSP_FW: case DATA_TYPE_SOC_APP: - if (aw_bin->header_info[i].valid_data_len & 0x01) { - ret = -EINVAL; - goto parse_bin_failed; - } + if (aw_bin->header_info[i].valid_data_len & 0x01) + return -EINVAL; swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); @@ -422,20 +419,17 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * } } prof_desc->prof_st = AW88395_PROFILE_OK; - ret = 0; -parse_bin_failed: - devm_kfree(aw_dev->dev, aw_bin); - return ret; + return 0; } static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) { - struct aw_bin *aw_bin; int ret; - aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); + struct aw_bin *aw_bin __free(kfree) = kzalloc(data_len + sizeof(*aw_bin), + GFP_KERNEL); if (!aw_bin) return -ENOMEM; @@ -445,14 +439,13 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->dev, "parse bin failed"); - goto parse_bin_failed; + return ret; } if ((aw_bin->all_bin_parse_num != 1) || (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { dev_err(aw_dev->dev, "bin num or type error"); - ret = -EINVAL; - goto parse_bin_failed; + return -EINVAL; } prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = @@ -461,15 +454,7 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, aw_bin->header_info[0].valid_data_len; prof_desc->prof_st = AW88395_PROFILE_OK; - devm_kfree(aw_dev->dev, aw_bin); - aw_bin = NULL; - return 0; - -parse_bin_failed: - devm_kfree(aw_dev->dev, aw_bin); - aw_bin = NULL; - return ret; } static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, @@ -678,21 +663,21 @@ static int aw_dev_cfg_get_multiple_valid_prof(struct aw_device *aw_dev, static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr) { - struct aw_all_prof_info *all_prof_info; int ret; - all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*all_prof_info), + GFP_KERNEL); if (!all_prof_info) return -ENOMEM; ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) { - goto exit; + return ret; } else if (ret == AW88395_DEV_TYPE_NONE) { dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) - goto exit; + return ret; } switch (aw_dev->prof_data_type) { @@ -710,8 +695,6 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, if (!ret) aw_dev->prof_info.prof_name_list = profile_name; -exit: - devm_kfree(aw_dev->dev, all_prof_info); return ret; } diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 9fcb805bf971..8dc2b8aa6832 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -8,9 +8,9 @@ // #include <linux/crc32.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/firmware.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <sound/soc.h> #include "aw88399.h" @@ -1892,7 +1892,7 @@ static int aw88399_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88399_i2c_id[] = { - { AW88399_I2C_NAME, 0 }, + { AW88399_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id); diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index 4c517231d765..e63a518e3b8e 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -787,7 +787,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l34 = { .endianness = 1, }; -static struct regmap_config cs35l34_regmap = { +static const struct regmap_config cs35l34_regmap = { .reg_bits = 8, .val_bits = 8, diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index c39b3cfe9574..7a01b1d9fc9d 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1086,7 +1086,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l35 = { .endianness = 1, }; -static struct regmap_config cs35l35_regmap = { +static const struct regmap_config cs35l35_regmap = { .reg_bits = 8, .val_bits = 8, diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index bc79990615e8..cbea79bd8980 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1300,7 +1300,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { .endianness = 1, }; -static struct regmap_config cs35l36_regmap = { +static const struct regmap_config cs35l36_regmap = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index e9993a39f7d0..1702f26049d3 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -936,8 +936,8 @@ int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsign EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); int cs35l41_set_channels(struct device *dev, struct regmap *reg, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { unsigned int val, mask; int i; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index cb25c33cc9b9..1688c2c688f0 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -673,7 +673,8 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { }; static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, - unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) + const unsigned int *tx_slot, + unsigned int rx_n, const unsigned int *rx_slot) { struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 70ff55c1517f..fc03bb7ecae1 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -271,7 +271,6 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); prop->paging_support = true; - prop->clk_stop_mode1 = false; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; @@ -317,79 +316,6 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, return 0; } -static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, - struct sdw_slave *peripheral) -{ - unsigned int curr_scale_reg, next_scale_reg; - int curr_scale, next_scale, ret; - - if (!cs35l56->base.init_done) - return 0; - - if (peripheral->bus->params.curr_bank) { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - } else { - curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; - next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; - } - - /* - * Current clock scale value must be different to new value. - * Modify current to guarantee this. If next still has the dummy - * value we wrote when it was current, the core code has not set - * a new scale so restore its original good value - */ - curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); - if (curr_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); - return curr_scale; - } - - next_scale = sdw_read_no_pm(peripheral, next_scale_reg); - if (next_scale < 0) { - dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); - return next_scale; - } - - if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { - next_scale = cs35l56->old_sdw_clock_scale; - ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", - ret); - return ret; - } - } - - cs35l56->old_sdw_clock_scale = curr_scale; - ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); - return ret; - } - - dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); - - return 0; -} - -static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, - struct sdw_bus_params *params) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - int sclk; - - sclk = params->curr_dr_freq / 2; - dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", - __func__, sclk, params->col, params->row); - - if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0)) - return cs35l56_a1_kick_divider(cs35l56, peripheral); - - return 0; -} - static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -405,7 +331,6 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, - .bus_config = cs35l56_sdw_bus_config, #ifdef DEBUG .clk_stop = cs35l56_sdw_clk_stop, #endif diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 30497152e02a..e7e8d617da94 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -20,6 +20,18 @@ static const struct reg_sequence cs35l56_patch[] = { * Firmware can change these to non-defaults to satisfy SDCA. * Ensure that they are at known defaults. */ + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000000 }, + { CS35L56_ASP1TX2_INPUT, 0x00000000 }, + { CS35L56_ASP1TX3_INPUT, 0x00000000 }, + { CS35L56_ASP1TX4_INPUT, 0x00000000 }, { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, @@ -41,12 +53,18 @@ 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 */ - /* - * No defaults for ASP1 control or ASP1TX mixer. See - * cs35l56_populate_asp1_register_defaults() and - * cs35l56_sync_asp1_mixer_widgets_with_firmware(). - */ - + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000000 }, + { CS35L56_ASP1TX2_INPUT, 0x00000000 }, + { CS35L56_ASP1TX3_INPUT, 0x00000000 }, + { CS35L56_ASP1TX4_INPUT, 0x00000000 }, { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, @@ -206,77 +224,6 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } -static const struct reg_sequence cs35l56_asp1_defaults[] = { - REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000), - REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028), - REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200), - REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002), - REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100), - REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100), - REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018), - REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018), - REG_SEQ0(CS35L56_ASP1TX1_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX2_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX3_INPUT, 0x00000000), - REG_SEQ0(CS35L56_ASP1TX4_INPUT, 0x00000000), -}; - -/* - * The firmware can have control of the ASP so we don't provide regmap - * with defaults for these registers, to prevent a regcache_sync() from - * overwriting the firmware settings. But if the machine driver hooks up - * the ASP it means the driver is taking control of the ASP, so then the - * registers are populated with the defaults. - */ -int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base) -{ - if (!cs35l56_base->fw_owns_asp1) - return 0; - - cs35l56_base->fw_owns_asp1 = false; - - return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults, - ARRAY_SIZE(cs35l56_asp1_defaults)); -} -EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED); - -/* - * The firmware boot sequence can overwrite the ASP1 config registers so that - * they don't match regmap's view of their values. Rewrite the values from the - * regmap cache into the hardware registers. - */ -int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) -{ - struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)]; - int i, ret; - - if (cs35l56_base->fw_owns_asp1) - return 0; - - memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs)); - - /* Read current values from regmap cache into the write sequence */ - for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { - ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); - if (ret) - goto err; - } - - /* Write the values cache-bypassed so that they will be written to silicon */ - ret = regmap_multi_reg_write_bypassed(cs35l56_base->regmap, asp1_regs, - ARRAY_SIZE(asp1_regs)); - if (ret) - goto err; - - return 0; - -err: - dev_err(cs35l56_base->dev, "Failed to sync ASP1 registers: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_force_sync_asp1_registers_from_cache, SND_SOC_CS35L56_SHARED); - int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command) { unsigned int val; @@ -298,19 +245,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base) { int ret; - unsigned int reg; unsigned int val; ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN); if (ret) return ret; - if (cs35l56_base->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_PM_CUR_STATE_A1; - else - reg = CS35L56_DSP1_PM_CUR_STATE; - - ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, + ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP1_PM_CUR_STATE, val, (val == CS35L56_HALO_STATE_SHUTDOWN), CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US); @@ -323,15 +264,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, SND_SOC_CS35L56_SHARED); int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) { - unsigned int reg; unsigned int val = 0; int read_ret, poll_ret; - if (cs35l56_base->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_HALO_STATE_A1; - else - reg = CS35L56_DSP1_HALO_STATE; - /* * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read of the status register. @@ -341,7 +276,7 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US, false, - cs35l56_base->regmap, reg, &val); + cs35l56_base->regmap, CS35L56_DSP1_HALO_STATE, &val); if (poll_ret) { dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n", @@ -397,7 +332,7 @@ int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) { int ret; - if (!irq) + if (irq < 1) return 0; ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, @@ -779,11 +714,6 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) else cs35l56_wait_control_port_ready(); - /* - * The HALO_STATE register is in different locations on Ax and B0 - * devices so the REVID needs to be determined before waiting for the - * firmware to boot. - */ ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); @@ -857,9 +787,16 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED); int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base) { struct gpio_descs *descs; - int speaker_id; + u32 speaker_id; int i, ret; + /* Attempt to read the speaker type from a device property first */ + ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id); + if (!ret) { + dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id); + return speaker_id; + } + /* Read the speaker type qualifier from the motherboard GPIOs */ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN); if (!descs) { diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 758dfdf9d3ea..84c34f5b1a51 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -63,131 +63,6 @@ static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol, return snd_soc_put_volsw(kcontrol, ucontrol); } -static const unsigned short cs35l56_asp1_mixer_regs[] = { - CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX2_INPUT, - CS35L56_ASP1TX3_INPUT, CS35L56_ASP1TX4_INPUT, -}; - -static const char * const cs35l56_asp1_mux_control_names[] = { - "ASP1 TX1 Source", "ASP1 TX2 Source", "ASP1 TX3 Source", "ASP1 TX4 Source" -}; - -static int cs35l56_sync_asp1_mixer_widgets_with_firmware(struct cs35l56_private *cs35l56) -{ - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component); - const char *prefix = cs35l56->component->name_prefix; - char full_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - const char *name; - struct snd_kcontrol *kcontrol; - struct soc_enum *e; - unsigned int val[4]; - int i, item, ret; - - if (cs35l56->asp1_mixer_widgets_initialized) - return 0; - - /* - * Resume so we can read the registers from silicon if the regmap - * cache has not yet been populated. - */ - ret = pm_runtime_resume_and_get(cs35l56->base.dev); - if (ret < 0) - return ret; - - /* Wait for firmware download and reboot */ - cs35l56_wait_dsp_ready(cs35l56); - - ret = regmap_bulk_read(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, - val, ARRAY_SIZE(val)); - - pm_runtime_mark_last_busy(cs35l56->base.dev); - pm_runtime_put_autosuspend(cs35l56->base.dev); - - if (ret) { - dev_err(cs35l56->base.dev, "Failed to read ASP1 mixer regs: %d\n", ret); - return ret; - } - - for (i = 0; i < ARRAY_SIZE(cs35l56_asp1_mux_control_names); ++i) { - name = cs35l56_asp1_mux_control_names[i]; - - if (prefix) { - snprintf(full_name, sizeof(full_name), "%s %s", prefix, name); - name = full_name; - } - - kcontrol = snd_soc_card_get_kcontrol_locked(dapm->card, name); - if (!kcontrol) { - dev_warn(cs35l56->base.dev, "Could not find control %s\n", name); - continue; - } - - e = (struct soc_enum *)kcontrol->private_value; - item = snd_soc_enum_val_to_item(e, val[i] & CS35L56_ASP_TXn_SRC_MASK); - snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); - } - - cs35l56->asp1_mixer_widgets_initialized = true; - - return 0; -} - -static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int index = e->shift_l; - unsigned int addr, val; - int ret; - - ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); - if (ret) - return ret; - - addr = cs35l56_asp1_mixer_regs[index]; - ret = regmap_read(cs35l56->base.regmap, addr, &val); - if (ret) - return ret; - - val &= CS35L56_ASP_TXn_SRC_MASK; - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); - - return 0; -} - -static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int item = ucontrol->value.enumerated.item[0]; - int index = e->shift_l; - unsigned int addr, val; - bool changed; - int ret; - - ret = cs35l56_sync_asp1_mixer_widgets_with_firmware(cs35l56); - if (ret) - return ret; - - addr = cs35l56_asp1_mixer_regs[index]; - val = snd_soc_enum_item_to_val(e, item); - - ret = regmap_update_bits_check(cs35l56->base.regmap, addr, - CS35L56_ASP_TXn_SRC_MASK, val, &changed); - if (ret) - return ret; - - if (changed) - snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL); - - return changed; -} - static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); static const struct snd_kcontrol_new cs35l56_controls[] = { @@ -196,7 +71,11 @@ static const struct snd_kcontrol_new cs35l56_controls[] = { cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), SOC_SINGLE_S_EXT_TLV("Speaker Volume", CS35L56_MAIN_RENDER_USER_VOLUME, - 6, -400, 400, 9, 0, + CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT, + CS35L56_MAIN_RENDER_USER_VOLUME_MIN, + CS35L56_MAIN_RENDER_USER_VOLUME_MAX, + CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT, + 0, cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw, vol_tlv), @@ -206,44 +85,40 @@ static const struct snd_kcontrol_new cs35l56_controls[] = { }; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, - SND_SOC_NOPM, - 0, 0, + CS35L56_ASP1TX1_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx1_mux = - SOC_DAPM_ENUM_EXT("ASP1TX1 SRC", cs35l56_asp1tx1_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum, - SND_SOC_NOPM, - 1, 0, + CS35L56_ASP1TX2_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx2_mux = - SOC_DAPM_ENUM_EXT("ASP1TX2 SRC", cs35l56_asp1tx2_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum, - SND_SOC_NOPM, - 2, 0, + CS35L56_ASP1TX3_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx3_mux = - SOC_DAPM_ENUM_EXT("ASP1TX3 SRC", cs35l56_asp1tx3_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum, - SND_SOC_NOPM, - 3, 0, + CS35L56_ASP1TX4_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, cs35l56_tx_input_texts, cs35l56_tx_input_values); static const struct snd_kcontrol_new asp1_tx4_mux = - SOC_DAPM_ENUM_EXT("ASP1TX4 SRC", cs35l56_asp1tx4_enum, - cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put); + SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum); static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum, CS35L56_SWIRE_DP3_CH1_INPUT, @@ -281,21 +156,6 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, static const struct snd_kcontrol_new sdw1_tx4_mux = SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); -static int cs35l56_asp1_cfg_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - /* Override register values set by firmware boot */ - return cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base); - default: - return 0; - } -} - static int cs35l56_play_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -332,9 +192,6 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), - SND_SOC_DAPM_SUPPLY("ASP1 CFG", SND_SOC_NOPM, 0, 0, cs35l56_asp1_cfg_event, - SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -402,9 +259,6 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { { "AMP", NULL, "VDD_B" }, { "AMP", NULL, "VDD_AMP" }, - { "ASP1 Playback", NULL, "ASP1 CFG" }, - { "ASP1 Capture", NULL, "ASP1 CFG" }, - { "ASP1 Playback", NULL, "PLAY" }, { "SDW1 Playback", NULL, "PLAY" }, @@ -455,14 +309,9 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); unsigned int val; - int ret; dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; - switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; @@ -536,11 +385,6 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx unsigned int rx_mask, int slots, int slot_width) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; if ((slots == 0) || (slot_width == 0)) { dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); @@ -589,11 +433,6 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); unsigned int rate = params_rate(params); u8 asp_width, asp_wl; - int ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; asp_wl = params_width(params); if (cs35l56->asp_slot_width) @@ -650,11 +489,7 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int freq_id, ret; - - ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); - if (ret) - return ret; + int freq_id; if (freq == 0) { cs35l56->sysclk_set = false; @@ -1035,13 +870,6 @@ static int cs35l56_component_probe(struct snd_soc_component *component) debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); - /* - * The widgets for the ASP1TX mixer can't be initialized - * until the firmware has been downloaded and rebooted. - */ - regcache_drop_region(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX4_INPUT); - cs35l56->asp1_mixer_widgets_initialized = false; - queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); return 0; @@ -1432,9 +1260,6 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; - /* Assume that the firmware owns ASP1 until we know different */ - cs35l56->base.fw_owns_asp1 = true; - dev_set_drvdata(cs35l56->base.dev, cs35l56); cs35l56_fill_supply_names(cs35l56->supplies); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index b000e7365e40..8a987ec01507 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -51,8 +51,6 @@ struct cs35l56_private { u8 asp_slot_count; bool tdm_mode; bool sysclk_set; - bool asp1_mixer_widgets_initialized; - u8 old_sdw_clock_scale; }; extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c new file mode 100644 index 000000000000..56659bf735db --- /dev/null +++ b/sound/soc/codecs/cs530x-i2c.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include "cs530x.h" + +static const struct of_device_id cs530x_of_match[] = { + { + .compatible = "cirrus,cs5302", + .data = (void *)CS5302, + }, { + .compatible = "cirrus,cs5304", + .data = (void *)CS5304, + }, { + .compatible = "cirrus,cs5308", + .data = (void *)CS5308, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cs530x_of_match); + +static const struct i2c_device_id cs530x_i2c_id[] = { + { "cs5302", CS5302 }, + { "cs5304", CS5304 }, + { "cs5308", CS5308 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id); + +static int cs530x_i2c_probe(struct i2c_client *client) +{ + struct cs530x_priv *cs530x; + + cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL); + if (!cs530x) + return -ENOMEM; + + i2c_set_clientdata(client, cs530x); + + cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap); + if (IS_ERR(cs530x->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap), + "Failed to allocate register map\n"); + + cs530x->devtype = (uintptr_t)i2c_get_match_data(client); + cs530x->dev = &client->dev; + + return cs530x_probe(cs530x); +} + +static struct i2c_driver cs530x_i2c_driver = { + .driver = { + .name = "cs530x", + .of_match_table = cs530x_of_match, + }, + .probe = cs530x_i2c_probe, + .id_table = cs530x_i2c_id, +}; +module_i2c_driver(cs530x_i2c_driver); + +MODULE_DESCRIPTION("I2C CS530X driver"); +MODULE_IMPORT_NS(SND_SOC_CS530X); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paulha@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c new file mode 100644 index 000000000000..25a86a32e936 --- /dev/null +++ b/sound/soc/codecs/cs530x.c @@ -0,0 +1,971 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "cs530x.h" + +#define CS530X_MAX_ADC_CH 8 +#define CS530X_MIN_ADC_CH 2 + +static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = { + "vdd-a", + "vdd-io", +}; + +static const struct reg_default cs530x_reg_defaults[] = { + { CS530X_CLK_CFG_0, 0x30 }, + { CS530X_CLK_CFG_1, 0x0001 }, + { CS530X_CHIP_ENABLE, 0 }, + { CS530X_ASP_CFG, 0 }, + { CS530X_SIGNAL_PATH_CFG, 0 }, + { CS530X_IN_ENABLES, 0 }, + { CS530X_IN_RAMP_SUM, 0x0022 }, + { CS530X_IN_FILTER, 0 }, + { CS530X_IN_HIZ, 0 }, + { CS530X_IN_INV, 0 }, + { CS530X_IN_VOL_CTRL1_0, 0x8000 }, + { CS530X_IN_VOL_CTRL1_1, 0x8000 }, + { CS530X_IN_VOL_CTRL2_0, 0x8000 }, + { CS530X_IN_VOL_CTRL2_1, 0x8000 }, + { CS530X_IN_VOL_CTRL3_0, 0x8000 }, + { CS530X_IN_VOL_CTRL3_1, 0x8000 }, + { CS530X_IN_VOL_CTRL4_0, 0x8000 }, + { CS530X_IN_VOL_CTRL4_1, 0x8000 }, + { CS530X_PAD_FN, 0 }, + { CS530X_PAD_LVL, 0 }, +}; + +static bool cs530x_read_and_write_regs(unsigned int reg) +{ + switch (reg) { + case CS530X_CLK_CFG_0: + case CS530X_CLK_CFG_1: + case CS530X_CHIP_ENABLE: + case CS530X_ASP_CFG: + case CS530X_SIGNAL_PATH_CFG: + case CS530X_IN_ENABLES: + case CS530X_IN_RAMP_SUM: + case CS530X_IN_FILTER: + case CS530X_IN_HIZ: + case CS530X_IN_INV: + case CS530X_IN_VOL_CTRL1_0: + case CS530X_IN_VOL_CTRL1_1: + case CS530X_IN_VOL_CTRL2_0: + case CS530X_IN_VOL_CTRL2_1: + case CS530X_IN_VOL_CTRL3_0: + case CS530X_IN_VOL_CTRL3_1: + case CS530X_IN_VOL_CTRL4_0: + case CS530X_IN_VOL_CTRL4_1: + case CS530X_PAD_FN: + case CS530X_PAD_LVL: + return true; + default: + return false; + } +} + +static bool cs530x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_DEVID: + case CS530X_REVID: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static bool cs530x_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_SW_RESET: + case CS530X_IN_VOL_CTRL5: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static int cs530x_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret) + goto volsw_err; + + /* Write IN_VU bit for the volume change to take effect */ + regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU); + +volsw_err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0); + +static const char * const cs530x_in_hpf_text[] = { + "Min Phase Slow Roll-off", + "Min Phase Fast Roll-off", + "Linear Phase Slow Roll-off", + "Linear Phase Fast Roll-off", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_hpf_enum, CS530X_IN_FILTER, + CS530X_IN_FILTER_SHIFT, + cs530x_in_hpf_text); + +static const char * const cs530x_in_4ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_4ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum), +}; + +static const char * const cs530x_in_8ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", + "Groups of 8", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_8ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum), +}; + + +static const char * const cs530x_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_INC_SHIFT, + cs530x_vol_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_DEC_SHIFT, + cs530x_vol_ramp_text); + +static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = { +SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_ENUM("IN HPF Select", cs530x_in_hpf_enum), +SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum), +SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum), + +SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = { +SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = { +SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0), +}; + +static int cs530x_adc_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + cs530x->adc_pairs_count++; + break; + case SND_SOC_DAPM_POST_PMU: + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + + cs530x->adc_pairs_count--; + if (!cs530x->adc_pairs_count) { + usleep_range(1000, 1100); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + } + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_kcontrol_new adc12_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc34_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc56_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc78_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new in_hpf_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +/* General DAPM widgets for all devices */ +static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0), +}; + +/* ADC's Channels 1 and 2 plus generic ADC DAPM events */ +static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1"), +SND_SOC_DAPM_INPUT("IN2"), +SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0), +SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl), +SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT, + 0, &in_hpf_ctrl), +}; + +/* ADC's Channels 3 and 4 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN3"), +SND_SOC_DAPM_INPUT("IN4"), +SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0), +SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl), +}; + +/* ADC's Channels 5 to 8 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN5"), +SND_SOC_DAPM_INPUT("IN6"), +SND_SOC_DAPM_INPUT("IN7"), +SND_SOC_DAPM_INPUT("IN8"), +SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0), +SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl), +SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0), +SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl), +}; + +static const struct snd_soc_dapm_route adc_ch1_2_routes[] = { + { "ADC1", NULL, "Global Enable" }, + { "ADC2", NULL, "Global Enable" }, + + { "ADC12 Enable", "Switch", "IN1" }, + { "ADC12 Enable", "Switch", "IN2" }, + { "ADC1", NULL, "ADC12 Enable" }, + { "ADC2", NULL, "ADC12 Enable" }, + { "IN HPF", "Switch", "ADC1" }, + { "IN HPF", "Switch", "ADC2" }, + + { "AIF Capture", NULL, "IN HPF" }, + { "AIF Capture", NULL, "ADC1" }, + { "AIF Capture", NULL, "ADC2" }, +}; + +static const struct snd_soc_dapm_route adc_ch3_4_routes[] = { + { "ADC3", NULL, "Global Enable" }, + { "ADC4", NULL, "Global Enable" }, + + { "ADC34 Enable", "Switch", "IN3" }, + { "ADC34 Enable", "Switch", "IN4" }, + { "ADC3", NULL, "ADC34 Enable" }, + { "ADC4", NULL, "ADC34 Enable" }, + { "IN HPF", "Switch", "ADC3" }, + { "IN HPF", "Switch", "ADC4" }, + + { "AIF Capture", NULL, "ADC3" }, + { "AIF Capture", NULL, "ADC4" }, +}; + +static const struct snd_soc_dapm_route adc_ch5_8_routes[] = { + { "ADC5", NULL, "Global Enable" }, + { "ADC6", NULL, "Global Enable" }, + { "ADC7", NULL, "Global Enable" }, + { "ADC8", NULL, "Global Enable" }, + + { "ADC56 Enable", "Switch", "IN5" }, + { "ADC56 Enable", "Switch", "IN6" }, + { "ADC5", NULL, "ADC56 Enable" }, + { "ADC6", NULL, "ADC56 Enable" }, + { "IN HPF", "Switch", "ADC5" }, + { "IN HPF", "Switch", "ADC6" }, + + { "AIF Capture", NULL, "ADC5" }, + { "AIF Capture", NULL, "ADC6" }, + + { "ADC78 Enable", "Switch", "IN7" }, + { "ADC78 Enable", "Switch", "IN8" }, + { "ADC7", NULL, "ADC78 Enable" }, + { "ADC8", NULL, "ADC78 Enable" }, + { "IN HPF", "Switch", "ADC7" }, + { "IN HPF", "Switch", "ADC8" }, + + { "AIF Capture", NULL, "ADC7" }, + { "AIF Capture", NULL, "ADC8" }, +}; + +static void cs530x_add_12_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_1_to_2_controls, + ARRAY_SIZE(cs530x_in_1_to_2_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes, + ARRAY_SIZE(adc_ch1_2_routes)); +} + +static void cs530x_add_34_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_3_to_4_controls, + ARRAY_SIZE(cs530x_in_3_to_4_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes, + ARRAY_SIZE(adc_ch3_4_routes)); +} + +static int cs530x_set_bclk(struct snd_soc_component *component, const int freq) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int bclk_val; + + switch (freq) { + case 2822400: + case 3072000: + bclk_val = CS530X_BCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + bclk_val = CS530X_BCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + bclk_val = CS530X_BCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + bclk_val = CS530X_BCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid BCLK frequency %d\n", freq); + return -EINVAL; + } + + dev_dbg(component->dev, "BCLK frequency is %d\n", freq); + + return regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_BCLK_FREQ_MASK, bclk_val); +} + +static int cs530x_set_pll_refclk(struct snd_soc_component *component, + const unsigned int freq) +{ + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int refclk; + + switch (freq) { + case 2822400: + case 3072000: + refclk = CS530X_REFCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + refclk = CS530X_REFCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + refclk = CS530X_REFCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + refclk = CS530X_REFCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid PLL refclk %d\n", freq); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_FREQ_MASK, refclk); +} + +static int cs530x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret = 0, fs = params_rate(params), bclk; + unsigned int fs_val; + + + switch (fs) { + case 32000: + fs_val = CS530X_FS_32K; + break; + case 44100: + case 48000: + fs_val = CS530X_FS_48K_44P1K; + break; + case 88200: + case 96000: + fs_val = CS530X_FS_96K_88P2K; + break; + case 176400: + case 192000: + fs_val = CS530X_FS_192K_176P4K; + break; + case 356800: + case 384000: + fs_val = CS530X_FS_384K_356P8K; + break; + case 705600: + case 768000: + fs_val = CS530X_FS_768K_705P6K; + break; + default: + dev_err(component->dev, "Invalid sample rate %d\n", fs); + return -EINVAL; + } + + cs530x->fs = fs; + regmap_update_bits(regmap, CS530X_CLK_CFG_1, + CS530X_SAMPLE_RATE_MASK, fs_val); + + + if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_TDM_EN_MASK)) { + dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", + cs530x->tdm_slots, cs530x->tdm_width); + bclk = snd_soc_tdm_params_to_bclk(params, + cs530x->tdm_width, + cs530x->tdm_slots, + 1); + } else { + bclk = snd_soc_params_to_bclk(params); + } + + if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK)) { + ret = cs530x_set_pll_refclk(component, bclk); + if (ret) + return ret; + } + + return cs530x_set_bclk(component, bclk); +} + +static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int asp_fmt, asp_cfg = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + asp_cfg = CS530X_ASP_PRIMARY; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = CS530X_ASP_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = CS530X_ASP_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + asp_fmt = CS530X_ASP_FMT_LJ; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + asp_cfg |= CS530X_ASP_BCLK_INV; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV, + asp_cfg); + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_FMT_MASK, asp_fmt); +} + +static bool cs530x_check_mclk_freq(struct snd_soc_component *component, + const unsigned int freq) +{ + switch (freq) { + case 24576000: + case 22579200: + case 12288000: + case 11289600: + return true; + default: + dev_err(component->dev, "Invalid MCLK %d\n", freq); + return false; + } +} + +static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int val; + + switch (tx_mask) { + case CS530X_0_1_TDM_SLOT_MASK: + case CS530X_0_3_TDM_SLOT_MASK: + case CS530X_0_7_TDM_SLOT_MASK: + val = CS530X_0_7_TDM_SLOT_VAL; + break; + case CS530X_2_3_TDM_SLOT_MASK: + val = CS530X_2_3_TDM_SLOT_VAL; + break; + case CS530X_4_5_TDM_SLOT_MASK: + case CS530X_4_7_TDM_SLOT_MASK: + val = CS530X_4_7_TDM_SLOT_VAL; + break; + case CS530X_6_7_TDM_SLOT_MASK: + val = CS530X_6_7_TDM_SLOT_VAL; + break; + case CS530X_8_9_TDM_SLOT_MASK: + case CS530X_8_11_TDM_SLOT_MASK: + case CS530X_8_15_TDM_SLOT_MASK: + val = CS530X_8_15_TDM_SLOT_VAL; + break; + case CS530X_10_11_TDM_SLOT_MASK: + val = CS530X_10_11_TDM_SLOT_VAL; + break; + case CS530X_12_13_TDM_SLOT_MASK: + case CS530X_12_15_TDM_SLOT_MASK: + val = CS530X_12_15_TDM_SLOT_VAL; + break; + case CS530X_14_15_TDM_SLOT_MASK: + val = CS530X_14_15_TDM_SLOT_VAL; + break; + default: + dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask); + return -EINVAL; + } + + cs530x->tdm_width = slot_width; + cs530x->tdm_slots = slots; + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_TDM_SLOT_MASK, + val << CS530X_ASP_TDM_SLOT_SHIFT); +} + +static const struct snd_soc_dai_ops cs530x_dai_ops = { + .set_fmt = cs530x_set_fmt, + .hw_params = cs530x_hw_params, + .set_tdm_slot = cs530x_set_tdm_slot, +}; + +static const struct snd_soc_dai_driver cs530x_dai = { + .name = "cs530x-dai", + .capture = { + .stream_name = "AIF Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &cs530x_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static int cs530x_set_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int sysclk_src; + int ret; + + regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src); + + /* Check if the source is the PLL */ + if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0) + return 0; + + switch (source) { + case CS530X_PLL_SRC_MCLK: + if (!cs530x_check_mclk_freq(component, freq_in)) + return -EINVAL; + + ret = cs530x_set_pll_refclk(component, freq_in); + if (ret) + return ret; + + break; + case CS530X_PLL_SRC_BCLK: + break; + default: + dev_err(component->dev, "Invalid PLL source %d\n", source); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK, source); +} + +static int cs530x_component_probe(struct snd_soc_component *component) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int num_widgets; + + snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets, + ARRAY_SIZE(cs530x_gen_dapm_widgets)); + + switch (cs530x->devtype) { + case CS5302: + cs530x_add_12_adc_widgets(component); + break; + case CS5304: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_4ch_controls, + num_widgets); + break; + + case CS5308: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls); + snd_soc_add_component_controls(component, + cs530x_in_5_to_8_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_8ch_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets); + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets, + num_widgets); + + snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes, + ARRAY_SIZE(adc_ch5_8_routes)); + break; + default: + dev_err(component->dev, "Invalid device type %d\n", + cs530x->devtype); + return -EINVAL; + } + + return 0; +} + +static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (source) { + case CS530X_SYSCLK_SRC_MCLK: + if (freq != 24560000 && freq != 22572000) { + dev_err(component->dev, "Invalid MCLK source rate %d\n", + freq); + return -EINVAL; + } + + cs530x->mclk_rate = freq; + break; + case CS530X_SYSCLK_SRC_PLL: + break; + default: + dev_err(component->dev, "Invalid clock id %d\n", clk_id); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_SYSCLK_SRC_MASK, + source << CS530X_SYSCLK_SRC_SHIFT); +} + +static const struct snd_soc_component_driver soc_component_dev_cs530x = { + .probe = cs530x_component_probe, + .set_sysclk = cs530x_set_sysclk, + .set_pll = cs530x_set_pll, + .endianness = 1, +}; + +const struct regmap_config cs530x_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = CS530X_MAX_REGISTER, + .readable_reg = cs530x_readable_register, + .writeable_reg = cs530x_writeable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs530x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults), +}; +EXPORT_SYMBOL_NS_GPL(cs530x_regmap, SND_SOC_CS530X); + +static int cs530x_check_device_id(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + unsigned int dev_id, rev; + int ret; + + ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id); + if (ret) + return dev_err_probe(dev, ret, "Can't read device ID\n"); + + ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev); + if (ret) + return dev_err_probe(dev, ret, "Can't read REV ID\n"); + + dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev); + + switch (dev_id) { + case CS530X_2CH_ADC_DEV_ID: + cs530x->num_adcs = 2; + break; + case CS530X_4CH_ADC_DEV_ID: + cs530x->num_adcs = 4; + break; + case CS530X_8CH_ADC_DEV_ID: + cs530x->num_adcs = 8; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n", + dev_id); + } + + return 0; +} + +static int cs530x_parse_device_properties(struct cs530x_priv *cs530x) +{ + struct regmap *regmap = cs530x->regmap; + struct device *dev = cs530x->dev; + unsigned int val = 0; + + switch (cs530x->num_adcs) { + case 8: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin78")) + val = CS530X_IN78_HIZ; + + if (device_property_read_bool(dev, "cirrus,in-hiz-pin56")) + val |= CS530X_IN56_HIZ; + + fallthrough; + case 4: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin34")) + val |= CS530X_IN34_HIZ; + + fallthrough; + case 2: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin12")) + val |= CS530X_IN12_HIZ; + + return regmap_set_bits(regmap, CS530X_IN_HIZ, val); + default: + return dev_err_probe(dev, -EINVAL, + "Invalid number of adcs %d\n", + cs530x->num_adcs); + } +} + +int cs530x_probe(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + int ret, i; + + cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai, + sizeof(*(cs530x->dev_dai)), + GFP_KERNEL); + if (!cs530x->dev_dai) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++) + cs530x->supplies[i].supply = cs530x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to request supplies"); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to enable supplies"); + + cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(cs530x->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio), + "Reset gpio not available\n"); + goto err_regulator; + } + + if (cs530x->reset_gpio) { + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs530x->reset_gpio, 0); + } + + usleep_range(5000, 5100); + ret = cs530x_check_device_id(cs530x); + if (ret) + goto err_reset; + + if (!cs530x->reset_gpio) { + ret = regmap_write(cs530x->regmap, CS530X_SW_RESET, + CS530X_SW_RST_VAL); + if (ret) { + dev_err_probe(dev, ret, "Soft Reset Failed\n"); + goto err_reset; + } + } + + ret = cs530x_parse_device_properties(cs530x); + if (ret) + goto err_reset; + + cs530x->dev_dai->capture.channels_max = cs530x->num_adcs; + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_cs530x, cs530x->dev_dai, 1); + if (ret) { + dev_err_probe(dev, ret, "Can't register cs530x component\n"); + goto err_reset; + } + + return 0; + +err_reset: + gpiod_set_value_cansleep(cs530x->reset_gpio, 1); + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs530x_probe, SND_SOC_CS530X); + +MODULE_DESCRIPTION("CS530X CODEC Driver"); +MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h new file mode 100644 index 000000000000..f473e33eb835 --- /dev/null +++ b/sound/soc/codecs/cs530x.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS530x CODEC driver internal data + * + * Copyright (C) 2023-2024 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef _CS530X_H +#define _CS530X_H + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +/* Devices */ +#define CS530X_2CH_ADC_DEV_ID 0x5302 +#define CS530X_4CH_ADC_DEV_ID 0x5304 +#define CS530X_8CH_ADC_DEV_ID 0x5308 + +/* Registers */ + +#define CS530X_DEVID 0x0000000 +#define CS530X_REVID 0x0000004 +#define CS530X_SW_RESET 0x0000022 + +#define CS530X_CLK_CFG_0 0x0000040 +#define CS530X_CLK_CFG_1 0x0000042 +#define CS530X_CHIP_ENABLE 0x0000044 +#define CS530X_ASP_CFG 0x0000048 +#define CS530X_SIGNAL_PATH_CFG 0x0000050 +#define CS530X_IN_ENABLES 0x0000080 +#define CS530X_IN_RAMP_SUM 0x0000082 +#define CS530X_IN_FILTER 0x0000086 +#define CS530X_IN_HIZ 0x0000088 +#define CS530X_IN_INV 0x000008A +#define CS530X_IN_VOL_CTRL1_0 0x0000090 +#define CS530X_IN_VOL_CTRL1_1 0x0000092 +#define CS530X_IN_VOL_CTRL2_0 0x0000094 +#define CS530X_IN_VOL_CTRL2_1 0x0000096 +#define CS530X_IN_VOL_CTRL3_0 0x0000098 +#define CS530X_IN_VOL_CTRL3_1 0x000009A +#define CS530X_IN_VOL_CTRL4_0 0x000009C +#define CS530X_IN_VOL_CTRL4_1 0x000009E +#define CS530X_IN_VOL_CTRL5 0x00000A0 + +#define CS530X_PAD_FN 0x0003D24 +#define CS530X_PAD_LVL 0x0003D28 + +#define CS530X_MAX_REGISTER CS530X_PAD_LVL + +/* Register Fields */ + +/* REVID */ +#define CS530X_MTLREVID GENMASK(3, 0) +#define CS530X_AREVID GENMASK(7, 4) + +/* SW_RESET */ +#define CS530X_SW_RST_SHIFT 8 +#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT) + +/* CLK_CFG_0 */ +#define CS530X_PLL_REFCLK_SRC_MASK BIT(0) +#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4) +#define CS530X_SYSCLK_SRC_MASK BIT(12) +#define CS530X_SYSCLK_SRC_SHIFT 12 +#define CS530X_REFCLK_2P822_3P072 0 +#define CS530X_REFCLK_5P6448_6P144 0x10 +#define CS530X_REFCLK_11P2896_12P288 0x20 +#define CS530X_REFCLK_24P5792_24P576 0x30 + +/* CLK_CFG_1 */ +#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0) +#define CS530X_FS_32K 0 +#define CS530X_FS_48K_44P1K 1 +#define CS530X_FS_96K_88P2K 2 +#define CS530X_FS_192K_176P4K 3 +#define CS530X_FS_384K_356P8K 4 +#define CS530X_FS_768K_705P6K 5 + +/* CHIP_ENABLE */ +#define CS530X_GLOBAL_EN BIT(0) + +/* ASP_CFG */ +#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0) +#define CS530X_ASP_PRIMARY BIT(5) +#define CS530X_ASP_BCLK_INV BIT(6) +#define CS530X_BCLK_2P822_3P072 0 +#define CS530X_BCLK_5P6448_6P144 1 +#define CS530X_BCLK_11P2896_12P288 2 +#define CS530X_BCLK_24P5792_24P576 3 + +/* SIGNAL_PATH_CFG */ +#define CS530X_ASP_FMT_MASK GENMASK(2, 0) +#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3) +#define CS530X_ASP_TDM_SLOT_SHIFT 3 +#define CS530X_ASP_CH_REVERSE BIT(9) +#define CS530X_TDM_EN_MASK BIT(2) +#define CS530X_ASP_FMT_I2S 0 +#define CS530X_ASP_FMT_LJ 1 +#define CS530X_ASP_FMT_DSP_A 0x6 + +/* TDM Slots */ +#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0) +#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0) +#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0) +#define CS530X_0_7_TDM_SLOT_VAL 0 + +#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2) +#define CS530X_2_3_TDM_SLOT_VAL 1 + +#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4) +#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4) +#define CS530X_4_7_TDM_SLOT_VAL 2 + +#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6) +#define CS530X_6_7_TDM_SLOT_VAL 3 + +#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8) +#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8) +#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8) +#define CS530X_8_15_TDM_SLOT_VAL 4 + +#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10) +#define CS530X_10_11_TDM_SLOT_VAL 5 + +#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12) +#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12) +#define CS530X_12_15_TDM_SLOT_VAL 6 + +#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14) +#define CS530X_14_15_TDM_SLOT_VAL 7 + +/* IN_RAMP_SUM */ +#define CS530X_RAMP_RATE_INC_SHIFT 0 +#define CS530X_RAMP_RATE_DEC_SHIFT 4 +#define CS530X_IN_SUM_MODE_SHIFT 13 + +/* IN_FILTER */ +#define CS530X_IN_FILTER_SHIFT 8 +#define CS530X_IN_HPF_EN_SHIFT 12 + +/* IN_HIZ */ +#define CS530X_IN12_HIZ BIT(0) +#define CS530X_IN34_HIZ BIT(1) +#define CS530X_IN56_HIZ BIT(2) +#define CS530X_IN78_HIZ BIT(3) + +/* IN_INV */ +#define CS530X_IN1_INV_SHIFT 0 +#define CS530X_IN2_INV_SHIFT 1 +#define CS530X_IN3_INV_SHIFT 2 +#define CS530X_IN4_INV_SHIFT 3 +#define CS530X_IN5_INV_SHIFT 4 +#define CS530X_IN6_INV_SHIFT 5 +#define CS530X_IN7_INV_SHIFT 6 +#define CS530X_IN8_INV_SHIFT 7 + +/* IN_VOL_CTLy_z */ +#define CS530X_IN_MUTE BIT(15) + +/* IN_VOL_CTL5 */ +#define CS530X_IN_VU BIT(0) + +/* PAD_FN */ +#define CS530X_DOUT2_FN BIT(0) +#define CS530X_DOUT3_FN BIT(1) +#define CS530X_DOUT4_FN BIT(2) +#define CS530X_SPI_CS_FN BIT(3) +#define CS530X_CONFIG2_FN BIT(6) +#define CS530X_CONFIG3_FN BIT(7) +#define CS530X_CONFIG4_FN BIT(8) +#define CS530X_CONFIG5_FN BIT(9) + +/* PAD_LVL */ +#define CS530X_CONFIG2_LVL BIT(6) +#define CS530X_CONFIG3_LVL BIT(7) +#define CS530X_CONFIG4_LVL BIT(8) +#define CS530X_CONFIG5_LVL BIT(9) + +/* System Clock Source */ +#define CS530X_SYSCLK_SRC_MCLK 0 +#define CS530X_SYSCLK_SRC_PLL 1 + +/* PLL Reference Clock Source */ +#define CS530X_PLL_SRC_BCLK 0 +#define CS530X_PLL_SRC_MCLK 1 + +#define CS530X_NUM_SUPPLIES 2 + +enum cs530x_type { + CS5302, + CS5304, + CS5308, +}; + +/* codec private data */ +struct cs530x_priv { + struct regmap *regmap; + struct device *dev; + struct snd_soc_dai_driver *dev_dai; + + enum cs530x_type devtype; + int num_adcs; + int num_dacs; + + struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES]; + + unsigned int mclk_rate; + + int tdm_width; + int tdm_slots; + int fs; + int adc_pairs_count; + + struct gpio_desc *reset_gpio; +}; + +extern const struct regmap_config cs530x_regmap; +int cs530x_probe(struct cs530x_priv *cs530x); + +#endif diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index c0893146423b..bcbaf28a0b2d 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <sound/pcm_params.h> @@ -901,7 +900,7 @@ static const struct snd_soc_component_driver cs53l30_driver = { .endianness = 1, }; -static struct regmap_config cs53l30_regmap = { +static const struct regmap_config cs53l30_regmap = { .reg_bits = 8, .val_bits = 8, diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index e8e22b1a1963..8cfec8dcf839 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -63,11 +63,6 @@ static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0); -struct cx2072x_eq_ctrl { - u8 ch; - u8 band; -}; - static const DECLARE_TLV_DB_RANGE(hpf_tlv, 0, 0, TLV_DB_SCALE_ITEM(120, 0, 0), 1, 63, TLV_DB_SCALE_ITEM(30, 30, 0) diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a2b328f3b39f..f3ef6fb55304 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1720,7 +1720,7 @@ static int da7213_set_component_pll(struct snd_soc_component *component, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 da7213_dai_formats = +static const u64 da7213_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c new file mode 100644 index 000000000000..f557e33c26ad --- /dev/null +++ b/sound/soc/codecs/es8311.c @@ -0,0 +1,973 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8311.c -- es8311 ALSA SoC audio driver + * + * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com> + * + * Author: Matteo Martelli <matteomartelli3@gmail.com> + */ + +#include "linux/array_size.h" +#include "sound/pcm.h" +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "es8311.h" + +#define ES8311_NUM_RATES 10 +#define ES8311_RATES (SNDRV_PCM_RATE_8000_96000) +#define ES8311_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct es8311_priv { + struct regmap *regmap; + struct clk *mclk; + unsigned long mclk_freq; + bool provider; + unsigned int rates[ES8311_NUM_RATES]; + struct snd_pcm_hw_constraint_list constraints; +}; + +static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0); +static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0); + +#define ES8311_DB_LRCK_STEPS \ + "0.25db/4LRCK", \ + "0.25db/8LRCK", \ + "0.25db/16LRCK", \ + "0.25db/32LRCK", \ + "0.25db/64LRCK", \ + "0.25db/128LRCK", \ + "0.25db/256LRCK", \ + "0.25db/512LRCK", \ + "0.25db/1024LRCK", \ + "0.25db/2048LRCK", \ + "0.25db/4096LRCK", \ + "0.25db/8192LRCK", \ + "0.25db/16384LRCK", \ + "0.25db/32768LRCK", \ + "0.25db/65536LRCK", + +static const char *const es8311_level_winsize_txt[] = { + "0.25db/2LRCK", + ES8311_DB_LRCK_STEPS +}; + +static SOC_ENUM_SINGLE_DECL( + es8311_alc_winsize, ES8311_ADC4, + ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt); +static const DECLARE_TLV_DB_RANGE(es8311_level_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0), + 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0), + 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0), + 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0), + 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0), + 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0), +); + +static const char *const es8311_ramprate_txt[] = { + "Disabled", + ES8311_DB_LRCK_STEPS +}; +static SOC_ENUM_SINGLE_DECL( + es8311_adc_ramprate, ES8311_ADC1, + ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt); + +static const char *const es8311_automute_winsize_txt[] = { + "2048 samples", + "4096 samples", + "6144 samples", + "8192 samples", + "10240 samples", + "12288 samples", + "14336 samples", + "16384 samples", + "18432 samples", + "20480 samples", + "22528 samples", + "24576 samples", + "26624 samples", + "28672 samples", + "30720 samples", + "32768 samples", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_automute_winsize, ES8311_ADC6, + ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt); +static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0), + 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0), +); +static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0); + +static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0); +static SOC_ENUM_SINGLE_DECL( + es8311_drc_winsize, ES8311_DAC4, + ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt); +static SOC_ENUM_SINGLE_DECL( + es8311_dac_ramprate, ES8311_DAC6, + ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt); + +static const char *const es8311_out_mode_txt[] = { + "Lineout", + "Headphones" +}; +static SOC_ENUM_SINGLE_DECL( + es8311_out_mode, ES8311_SYS9, + ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt); + +static const struct snd_kcontrol_new es8311_snd_controls[] = { + /* Capture path */ + SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10, + ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0, + es8311_pga_gain_tlv), + SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2, + ES8311_ADC2_INV_SHIFT, 1, 0), + SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2, + ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0, + es8311_adc_scale_tlv), + SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3, + ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0, + es8311_adc_vol_tlv), + SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate), + SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4, + ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize), + SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6, + ES8311_ADC6_AUTOMUTE_NG_SHIFT, + ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv), + SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7, + ES8311_ADC7_AUTOMUTE_VOL_SHIFT, + ES8311_ADC7_AUTOMUTE_VOL_MAX, 0, + es8311_automute_vol_tlv), + SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT, + 1, 0), + SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8, + ES8311_ADC8_EQBYPASS_SHIFT, 1, 1), + SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT, + 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5, + ES8311_ADC5_ALC_MAXLEVEL_SHIFT, + ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5, + ES8311_ADC5_ALC_MINLEVEL_SHIFT, + ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv), + SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize), + + /* Playback path */ + SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0, + ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv), + SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT, + 1, 0), + SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5, + ES8311_DAC5_DRC_MAXLEVEL_SHIFT, + ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv), + SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5, + ES8311_DAC5_DRC_MINLEVEL_SHIFT, + ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv), + SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize), + SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate), + SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6, + ES8311_DAC6_EQBYPASS_SHIFT, 1, 1), + + SOC_ENUM("Output Mode", es8311_out_mode), +}; + +static const char *const es8311_diff_src_txt[] = { + "Disabled", + "MIC1P-MIC1N", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_diff_src_enum, ES8311_SYS10, + ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt); +static const struct snd_kcontrol_new es8311_diff_src_mux = + SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum); + +static const char *const es8311_dmic_src_txt[] = { + "Disabled", + "DMIC from MIC1P", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_dmic_src_enum, ES8311_SYS10, + ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt); +static const struct snd_kcontrol_new es8311_dmic_src_mux = + SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum); + +static const char * const es8311_aif1tx_src_txt[] = { + "ADC + ADC", + "ADC + 0", + "0 + ADC", + "0 + 0", + "DACL + ADC", + "ADC + DACR", + "DACL + DACR", +}; +static SOC_ENUM_SINGLE_DECL( + es8311_aif1tx_src_enum, ES8311_GPIO, + ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt); +static const struct snd_kcontrol_new es8311_aif1tx_src_mux = + SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum); + +static const char * const es8311_dac_src_txt[] = { + "Left", + "Right" +}; +static SOC_ENUM_SINGLE_DECL( + es8311_dac_src_enum, ES8311_SDP_IN, + ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt); +static const struct snd_kcontrol_new es8311_dac_src_mux = + SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum); + +static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT, + 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3, + ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1, + NULL, 0), + + /* Capture path */ + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8311_diff_src_mux), + SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3, + ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3, + ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL, + 0), + SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4, + ES8311_SYS4_PDN_MOD_SHIFT, 1), + SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, + &es8311_dmic_src_mux), + SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0, + &es8311_aif1tx_src_mux), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT, + ES8311_SDP_MUTE_SHIFT, 1), + + /* Playback path */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN, + ES8311_SDP_MUTE_SHIFT, 1), + SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0, + &es8311_dac_src_mux), + SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8, + ES8311_SYS8_PDN_DAC_SHIFT, 1), + SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1, + ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3, + ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route es8311_dapm_routes[] = { + /* Capture Path */ + { "MIC1", NULL, "Bias" }, + { "MIC1", NULL, "Analog power" }, + { "MIC1", NULL, "Vref" }, + { "Differential Mux", "MIC1P-MIC1N", "MIC1" }, + { "PGA", NULL, "Differential Mux" }, + { "Mono ADC", NULL, "PGA" }, + { "Mono ADC", NULL, "ADC Bias Gen" }, + { "Mono ADC", NULL, "ADC Vref Gen" }, + { "Mono ADC", NULL, "ADC Clock" }, + { "Mono ADC", NULL, "ADC Analog Clock" }, + { "Digital Mic Mux", "Disabled", "Mono ADC" }, + { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" }, + + { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" }, + { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" }, + + { "AIF1TX", NULL, "AIF1TX Source Mux" }, + + /* Playback Path */ + { "Mono DAC Source Mux", "Left", "AIF1RX" }, + { "Mono DAC Source Mux", "Right", "AIF1RX" }, + { "Mono DAC", NULL, "Mono DAC Source Mux" }, + { "Mono DAC", NULL, "DAC Clock" }, + { "Mono DAC", NULL, "DAC Analog Clock" }, + { "OUT", NULL, "Mono DAC" }, + { "OUT", NULL, "Bias" }, + { "OUT", NULL, "Analog power" }, + { "OUT", NULL, "Vref" }, + { "OUT", NULL, "DAC Vref Gen" }, +}; + +/* Bit clock divider values: + * from 1 to 20: the register takes the div value - 1 + * above 20: the register takes the corresponding idx of the div value + * in the following table + 20 + */ +#define ES8311_BCLK_DIV_IDX_OFFSET 20 +static const unsigned int es8311_bclk_divs[] = { + 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72 +}; + +struct es8311_mclk_coeff { + unsigned int rate; + unsigned int mclk; + unsigned int div; + unsigned int mult; + unsigned int div_adc_dac; +}; + +#define ES8311_MCLK_MAX_FREQ 49200000 + +/* Coefficients for common master clock frequencies based on clock table from + * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal + * to 256. This to keep the default adc and dac oversampling and adc scale + * settings. Internal mclk dividers and multipliers are dynamically adjusted to + * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed + * mclks frequencies (see es8311_cmp_adj_mclk_coeff). + * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512 + * (upper limit due to max mclk freq of 49.2MHz). + */ +static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = { + { 8000, 2048000, 1, 1, 1 }, + { 8000, 6144000, 3, 1, 1 }, + { 8000, 18432000, 3, 1, 3 }, + { 11025, 2822400, 1, 1, 1 }, + { 11025, 8467200, 3, 1, 1 }, + { 16000, 4096000, 1, 1, 1 }, + { 16000, 12288000, 3, 1, 1 }, + { 16000, 18432000, 3, 2, 3 }, + { 22050, 5644800, 1, 1, 1 }, + { 22050, 16934400, 3, 1, 1 }, + { 32000, 8192000, 1, 1, 1 }, + { 32000, 12288000, 3, 2, 1 }, + { 32000, 18432000, 3, 4, 3 }, + { 44100, 11289600, 1, 1, 1 }, + { 44100, 33868800, 3, 1, 1 }, + { 48000, 12288000, 1, 1, 1 }, + { 48000, 18432000, 3, 2, 1 }, + { 64000, 8192000, 1, 2, 1 }, + { 64000, 12288000, 3, 4, 1 }, + { 88200, 11289600, 1, 2, 1 }, + { 88200, 33868800, 3, 2, 1 }, + { 96000, 12288000, 1, 2, 1 }, + { 96000, 18432000, 3, 4, 1 }, +}; + +/* Compare coeff with provided mclk_freq and adjust it if needed. + * If frequencies match, return 0 and the unaltered coeff copy into out_coeff. + * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and + * the adjusted coeff copy into out_coeff. + * Return -EINVAL otherwise. + */ +static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq, + const struct es8311_mclk_coeff *coeff, + struct es8311_mclk_coeff *out_coeff) +{ + if (WARN_ON_ONCE(!coeff)) + return -EINVAL; + + unsigned int div = coeff->div; + unsigned int mult = coeff->mult; + bool match = false; + + if (coeff->mclk == mclk_freq) { + match = true; + } else if (mclk_freq % coeff->mclk == 0) { + div = mclk_freq / coeff->mclk; + div *= coeff->div; + if (div <= 8) + match = true; + } else if (coeff->mclk % mclk_freq == 0) { + mult = coeff->mclk / mclk_freq; + if (mult == 2 || mult == 4 || mult == 8) { + mult *= coeff->mult; + if (mult <= 8) + match = true; + } + } + if (!match) + return -EINVAL; + if (out_coeff) { + *out_coeff = *coeff; + out_coeff->div = div; + out_coeff->mult = mult; + } + return 0; +} + +static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate, + struct es8311_mclk_coeff *out_coeff) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) { + const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; + + if (coeff->rate != rate) + continue; + + int ret = + es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff); + if (ret == 0) + return 0; + } + return -EINVAL; +} + +static void es8311_set_sysclk_constraints(unsigned int mclk_freq, + struct es8311_priv *es8311) +{ + unsigned int count = 0; + + for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) && + count < ARRAY_SIZE(es8311->rates); i++) { + const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i]; + + if (count > 0 && coeff->rate == es8311->rates[count - 1]) + continue; + + int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL); + if (ret == 0) + es8311->rates[count++] = coeff->rate; + } + if (count) { + es8311->constraints.list = es8311->rates; + es8311->constraints.count = count; + } +} + +static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + unsigned int mask = ES8311_DAC1_DAC_DSMMUTE | + ES8311_DAC1_DAC_DEMMUTE; + unsigned int val = mute ? mask : 0; + + regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val); + } + + return 0; +} + +static int es8311_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (es8311->constraints.list) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &es8311->constraints); + } + + return 0; +} + +static int es8311_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + unsigned int wl; + int par_width = params_width(params); + + switch (par_width) { + case 16: + wl = ES8311_SDP_WL_16; + break; + case 18: + wl = ES8311_SDP_WL_18; + break; + case 20: + wl = ES8311_SDP_WL_20; + break; + case 24: + wl = ES8311_SDP_WL_24; + break; + case 32: + wl = ES8311_SDP_WL_32; + break; + default: + return -EINVAL; + } + unsigned int width = (unsigned int)par_width; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, ES8311_SDP_IN, + ES8311_SDP_WL_MASK, + wl << ES8311_SDP_WL_SHIFT); + } else { + snd_soc_component_update_bits(component, ES8311_SDP_OUT, + ES8311_SDP_WL_MASK, + wl << ES8311_SDP_WL_SHIFT); + } + + if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) { + dev_err(component->dev, "mclk frequency %lu too high\n", + es8311->mclk_freq); + return -EINVAL; + } + + unsigned int mclk_freq = es8311->mclk_freq; + unsigned int rate = params_rate(params); + unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON; + + if (!mclk_freq) { + if (es8311->provider) { + dev_err(component->dev, + "mclk not configured, cannot run as master\n"); + return -EINVAL; + } + dev_dbg(component->dev, + "mclk not configured, use bclk as internal mclk\n"); + + clkmgr = ES8311_CLKMGR1_MCLK_SEL; + + mclk_freq = rate * width * 2; + } + + struct es8311_mclk_coeff coeff; + int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff); + if (ret) { + dev_err(component->dev, "unable to find mclk coefficient\n"); + return ret; + } + + unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON | + ES8311_CLKMGR1_BCLK_ON; + + clkmgr |= ES8311_CLKMGR1_BCLK_ON; + snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr); + + if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 || + coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8)) + return -EINVAL; + + unsigned int mult; + + switch (coeff.mult) { + case 1: + mult = 0; + break; + case 2: + mult = 1; + break; + case 4: + mult = 2; + break; + case 8: + mult = 3; + break; + default: + WARN_ON_ONCE(true); + return -EINVAL; + } + + mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK; + clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT | + mult << ES8311_CLKMGR2_MULT_PRE_SHIFT; + snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr); + + mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK; + clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT | + (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT; + snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr); + + if (es8311->provider) { + unsigned int div_lrclk = mclk_freq / rate; + + if (WARN_ON_ONCE(div_lrclk == 0 || + div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1)) + return -EINVAL; + + mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK; + clkmgr = (div_lrclk - 1) >> 8; + snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask, + clkmgr); + clkmgr = (div_lrclk - 1) & 0xFF; + snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr); + + if (div_lrclk % (2 * width) != 0) { + dev_err(component->dev, + "unable to divide mclk %u to generate bclk\n", + mclk_freq); + return -EINVAL; + } + + unsigned int div_bclk = div_lrclk / (2 * width); + + mask = ES8311_CLKMGR6_DIV_BCLK_MASK; + if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) { + clkmgr = div_bclk - 1; + } else { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) { + if (es8311_bclk_divs[i] == div_bclk) + break; + } + if (i == ARRAY_SIZE(es8311_bclk_divs)) { + dev_err(component->dev, + "bclk divider %u not supported\n", + div_bclk); + return -EINVAL; + } + + clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET; + } + snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, + clkmgr); + } + + return 0; +} + +static int es8311_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + if (freq > ES8311_MCLK_MAX_FREQ) { + dev_err(component->dev, "invalid frequency %u: too high\n", + freq); + return -EINVAL; + } + + if (es8311->mclk_freq == freq) + return 0; + + es8311->mclk_freq = freq; + es8311->constraints.list = NULL; + es8311->constraints.count = 0; + + if (freq == 0) + return 0; + + int ret = clk_set_rate(es8311->mclk, freq); + if (ret) { + dev_err(component->dev, "unable to set mclk rate\n"); + return ret; + } + + es8311_set_sysclk_constraints(freq, es8311); + + return ret; +} + +static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + /* Master mode */ + es8311->provider = true; + + snd_soc_component_update_bits(component, ES8311_RESET, + ES8311_RESET_MSC, + ES8311_RESET_MSC); + break; + case SND_SOC_DAIFMT_CBC_CFC: + /* Slave mode */ + es8311->provider = false; + snd_soc_component_update_bits(component, ES8311_RESET, + ES8311_RESET_MSC, 0); + break; + default: + return -EINVAL; + } + + unsigned int sdp = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sdp |= ES8311_SDP_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + sdp |= ES8311_SDP_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dev_err(component->dev, "right justified mode not supported\n"); + return -EINVAL; + case SND_SOC_DAIFMT_DSP_B: + sdp |= ES8311_SDP_LRP; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + sdp |= ES8311_SDP_FMT_DSP; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + dev_err(component->dev, + "inverted fsync not supported in dsp mode\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + unsigned int clkmgr = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + sdp |= ES8311_SDP_LRP; + break; + case SND_SOC_DAIFMT_IB_NF: + clkmgr |= ES8311_CLKMGR6_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + clkmgr |= ES8311_CLKMGR6_BCLK_INV; + sdp |= ES8311_SDP_LRP; + break; + default: + return -EINVAL; + } + + unsigned int mask = ES8311_CLKMGR6_BCLK_INV; + + snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr); + + mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP; + snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp); + snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp); + + return 0; +} + +static int es8311_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + int ret = clk_prepare_enable(es8311->mclk); + if (ret) { + dev_err(component->dev, + "unable to prepare mclk\n"); + return ret; + } + + snd_soc_component_update_bits( + component, ES8311_SYS3, + ES8311_SYS3_PDN_VMIDSEL_MASK, + ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED); + } + + break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(es8311->mclk); + snd_soc_component_update_bits( + component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK, + ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN); + break; + default: + break; + } + return 0; +} + +static const struct snd_soc_dai_ops es8311_dai_ops = { + .startup = es8311_startup, + .hw_params = es8311_hw_params, + .mute_stream = es8311_mute, + .set_sysclk = es8311_set_sysclk, + .set_fmt = es8311_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver es8311_dai = { + .name = "es8311", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ES8311_RATES, + .formats = ES8311_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ES8311_RATES, + .formats = ES8311_FORMATS, + }, + .ops = &es8311_dai_ops, + .symmetric_rate = 1, +}; + +static void es8311_reset(struct snd_soc_component *component, bool reset) +{ + /* Reset procedure: + * (1) power down state machine and reset codec blocks then, + * (2) after a short delay, power up state machine and leave reset mode. + * Specific delay is not documented, using the same as es8316. + */ + unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK; + + if (reset) { + /* Enter reset mode */ + snd_soc_component_update_bits(component, ES8311_RESET, mask, + ES8311_RESET_RST_MASK); + } else { + /* Leave reset mode */ + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, ES8311_RESET, mask, + ES8311_RESET_CSM_ON); + } +} + +static int es8311_suspend(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311_reset(component, true); + + regcache_cache_only(es8311->regmap, true); + regcache_mark_dirty(es8311->regmap); + + return 0; +} + +static int es8311_resume(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311_reset(component, false); + + regcache_cache_only(es8311->regmap, false); + regcache_sync(es8311->regmap); + + return 0; +} + +static int es8311_component_probe(struct snd_soc_component *component) +{ + struct es8311_priv *es8311; + + es8311 = snd_soc_component_get_drvdata(component); + + es8311->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(es8311->mclk)) { + dev_err(component->dev, "invalid mclk\n"); + return PTR_ERR(es8311->mclk); + } + + es8311->mclk_freq = clk_get_rate(es8311->mclk); + if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ) + es8311_set_sysclk_constraints(es8311->mclk_freq, es8311); + + es8311_reset(component, true); + es8311_reset(component, false); + + /* Set minimal power up time */ + snd_soc_component_write(component, ES8311_SYS1, 0); + snd_soc_component_write(component, ES8311_SYS2, 0); + + return 0; +} + +static const struct regmap_config es8311_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8311_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct snd_soc_component_driver es8311_component_driver = { + .probe = es8311_component_probe, + .suspend = es8311_suspend, + .resume = es8311_resume, + .set_bias_level = es8311_set_bias_level, + .controls = es8311_snd_controls, + .num_controls = ARRAY_SIZE(es8311_snd_controls), + .dapm_widgets = es8311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets), + .dapm_routes = es8311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int es8311_i2c_probe(struct i2c_client *i2c_client) +{ + struct es8311_priv *es8311; + + struct device *dev = &i2c_client->dev; + + es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL); + if (es8311 == NULL) + return -ENOMEM; + + es8311->regmap = + devm_regmap_init_i2c(i2c_client, &es8311_regmap_config); + if (IS_ERR(es8311->regmap)) + return PTR_ERR(es8311->regmap); + + i2c_set_clientdata(i2c_client, es8311); + + return devm_snd_soc_register_component(dev, &es8311_component_driver, + &es8311_dai, 1); +} + +static const struct i2c_device_id es8311_id[] = { + { "es8311" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8311_id); + +static const struct of_device_id es8311_of_match[] = { + { + .compatible = "everest,es8311", + }, + {} +}; +MODULE_DEVICE_TABLE(of, es8311_of_match); + +static struct i2c_driver es8311_i2c_driver = { + .driver = { + .name = "es8311", + .of_match_table = es8311_of_match, + }, + .probe = es8311_i2c_probe, + .id_table = es8311_id, +}; + +module_i2c_driver(es8311_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8311 driver"); +MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8311.h b/sound/soc/codecs/es8311.h new file mode 100644 index 000000000000..8a3105bb8443 --- /dev/null +++ b/sound/soc/codecs/es8311.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * es8311.c -- es8311 ALSA SoC audio driver + * + * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com> + * + * Author: Matteo Martelli <matteomartelli3@gmail.com> + */ + +#ifndef _ES8311_H +#define _ES8311_H + +#include <linux/bitops.h> + +#define ES8311_RESET 0x00 +#define ES8311_RESET_CSM_ON BIT(7) +#define ES8311_RESET_MSC BIT(6) +#define ES8311_RESET_RST_MASK GENMASK(4, 0) + +/* Clock Manager Registers */ +#define ES8311_CLKMGR1 0x01 +#define ES8311_CLKMGR1_MCLK_SEL BIT(7) +#define ES8311_CLKMGR1_MCLK_ON BIT(5) +#define ES8311_CLKMGR1_BCLK_ON BIT(4) +#define ES8311_CLKMGR1_CLKADC_ON_SHIFT 3 +#define ES8311_CLKMGR1_CLKDAC_ON_SHIFT 2 +#define ES8311_CLKMGR1_ANACLKADC_ON_SHIFT 1 +#define ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT 0 +#define ES8311_CLKMGR2 0x02 +#define ES8311_CLKMGR2_DIV_PRE_MASK GENMASK(7, 5) +#define ES8311_CLKMGR2_DIV_PRE_SHIFT 5 +#define ES8311_CLKMGR2_DIV_PRE_MAX 0x07 +#define ES8311_CLKMGR2_MULT_PRE_MASK GENMASK(4, 3) +#define ES8311_CLKMGR2_MULT_PRE_SHIFT 3 +#define ES8311_CLKMGR3 0x03 +#define ES8311_CLKMGR4 0x04 +#define ES8311_CLKMGR5 0x05 +#define ES8311_CLKMGR5_ADC_DIV_MASK GENMASK(7, 4) +#define ES8311_CLKMGR5_ADC_DIV_SHIFT 4 +#define ES8311_CLKMGR5_DAC_DIV_MASK GENMASK(3, 0) +#define ES8311_CLKMGR5_DAC_DIV_SHIFT 0 +#define ES8311_CLKMGR6 0x06 +#define ES8311_CLKMGR6_BCLK_INV BIT(5) +#define ES8311_CLKMGR6_DIV_BCLK_MASK GENMASK(4, 0) +#define ES8311_CLKMGR7 0x07 +#define ES8311_CLKMGR7_LRCLK_DIV_H_MASK GENMASK(3, 0) +#define ES8311_CLKMGR8 0x08 +#define ES8311_CLKMGR_LRCLK_DIV_MAX 0x0FFF + +/* SDP Mode Registers */ +#define ES8311_SDP_IN 0x09 +#define ES8311_SDP_IN_SEL_SHIFT 7 +#define ES8311_SDP_OUT 0x0A +/* Following values are the same for both SPD_IN and SDP_OUT */ +#define ES8311_SDP_MUTE_SHIFT 6 +#define ES8311_SDP_LRP BIT(5) +#define ES8311_SDP_WL_MASK GENMASK(4, 2) +#define ES8311_SDP_WL_SHIFT 2 +#define ES8311_SDP_WL_24 0x00 +#define ES8311_SDP_WL_20 0x01 +#define ES8311_SDP_WL_18 0x02 +#define ES8311_SDP_WL_16 0x03 +#define ES8311_SDP_WL_32 0x04 +#define ES8311_SDP_FMT_MASK GENMASK(1, 0) +#define ES8311_SDP_FMT_I2S 0x00 +#define ES8311_SDP_FMT_LEFT_J 0x01 +#define ES8311_SDP_FMT_DSP 0x03 + +/* System registers */ +#define ES8311_SYS1 0x0B +#define ES8311_SYS2 0x0C +#define ES8311_SYS3 0x0D +#define ES8311_SYS3_PDN_ANA_SHIFT 7 +#define ES8311_SYS3_PDN_IBIASGEN_SHIFT 6 +#define ES8311_SYS3_PDN_ADCBIASGEN_SHIFT 5 +#define ES8311_SYS3_PDN_ADCVREFGEN_SHIFT 4 +#define ES8311_SYS3_PDN_DACVREFGEN_SHIFT 3 +#define ES8311_SYS3_PDN_VREF_SHIFT 2 +#define ES8311_SYS3_PDN_VMIDSEL_MASK GENMASK(1, 0) +#define ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN 0 +#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED 1 +#define ES8311_SYS3_PDN_VMIDSEL_NORMAL_OPERATION 2 +#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_FAST_SPEED 3 +#define ES8311_SYS4 0x0E +#define ES8311_SYS4_PDN_PGA_SHIFT 6 +#define ES8311_SYS4_PDN_MOD_SHIFT 5 +#define ES8311_SYS5 0x0F +#define ES8311_SYS6 0x10 +#define ES8311_SYS7 0x11 +#define ES8311_SYS8 0x12 +#define ES8311_SYS8_PDN_DAC_SHIFT 1 +#define ES8311_SYS9 0x13 +#define ES8311_SYS9_HPSW_SHIFT 4 +#define ES8311_SYS10 0x14 +#define ES8311_SYS10_DMIC_ON_SHIFT 6 +#define ES8311_SYS10_LINESEL_SHIFT 4 +#define ES8311_SYS10_PGAGAIN_SHIFT 0 +#define ES8311_SYS10_PGAGAIN_MAX 0x0A + +/* ADC Registers*/ +#define ES8311_ADC1 0x15 +#define ES8311_ADC1_RAMPRATE_SHIFT 4 +#define ES8311_ADC2 0x16 +#define ES8311_ADC2_INV_SHIFT 4 +#define ES8311_ADC2_SCALE_SHIFT 0 +#define ES8311_ADC2_SCALE_MAX 0x07 +#define ES8311_ADC3 0x17 +#define ES8311_ADC3_VOLUME_SHIFT 0 +#define ES8311_ADC3_VOLUME_MAX 0xFF +#define ES8311_ADC4 0x18 +#define ES8311_ADC4_ALC_EN_SHIFT 7 +#define ES8311_ADC4_AUTOMUTE_EN_SHIFT 6 +#define ES8311_ADC4_ALC_WINSIZE_SHIFT 0 +#define ES8311_ADC5 0x19 +#define ES8311_ADC5_ALC_MAXLEVEL_SHIFT 4 +#define ES8311_ADC5_ALC_MAXLEVEL_MAX 0x0F +#define ES8311_ADC5_ALC_MINLEVEL_SHIFT 0 +#define ES8311_ADC5_ALC_MINLEVEL_MAX 0x0F +#define ES8311_ADC6 0x1A +#define ES8311_ADC6_AUTOMUTE_WS_SHIFT 4 +#define ES8311_ADC6_AUTOMUTE_NG_SHIFT 0 +#define ES8311_ADC6_AUTOMUTE_NG_MAX 0x0F + +#define ES8311_ADC7 0x1B +#define ES8311_ADC7_AUTOMUTE_VOL_SHIFT 5 +#define ES8311_ADC7_AUTOMUTE_VOL_MAX 0x07 +#define ES8311_ADC8 0x1C +#define ES8311_ADC8_EQBYPASS_SHIFT 6 +#define ES8311_ADC8_HPF_SHIFT 5 + +/* DAC Registers */ +#define ES8311_DAC1 0x31 +#define ES8311_DAC1_DAC_DSMMUTE BIT(6) +#define ES8311_DAC1_DAC_DEMMUTE BIT(5) +#define ES8311_DAC2 0x32 +#define ES8311_DAC2_VOLUME_MAX 0xFF +#define ES8311_DAC3 0x33 +#define ES8311_DAC4 0x34 +#define ES8311_DAC4_DRC_EN_SHIFT 7 +#define ES8311_DAC4_DRC_WINSIZE_SHIFT 0 +#define ES8311_DAC5 0x35 +#define ES8311_DAC5_DRC_MAXLEVEL_SHIFT 4 +#define ES8311_DAC5_DRC_MAXLEVEL_MAX 0x0F +#define ES8311_DAC5_DRC_MINLEVEL_SHIFT 0 +#define ES8311_DAC5_DRC_MINLEVEL_MAX 0x0F +#define ES8311_DAC6 0x37 +#define ES8311_DAC6_RAMPRATE_SHIFT 4 +#define ES8311_DAC6_EQBYPASS_SHIFT 3 + +/* GPIO Registers */ +#define ES8311_GPIO 0x44 +#define ES8311_GPIO_ADC2DAC_SEL_SHIFT 7 +#define ES8311_GPIO_ADCDAT_SEL_SHIFT 4 + +/* Chip Info Registers */ +#define ES8311_CHIPID1 0xFD /* 0x83 */ +#define ES8311_CHIPID2 0xFE /* 0x11 */ +#define ES8311_CHIPVER 0xFF + +#define ES8311_REG_MAX 0xFF + +#endif diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 6a4e42e5e35b..b246694ebb4f 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -329,11 +329,29 @@ static bool es8326_volatile_register(struct device *dev, unsigned int reg) } } +static bool es8326_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8326_BIAS_SW1: + case ES8326_BIAS_SW2: + case ES8326_BIAS_SW3: + case ES8326_BIAS_SW4: + case ES8326_ADC_HPFS1: + case ES8326_ADC_HPFS2: + return false; + default: + return true; + } +} + static const struct regmap_config es8326_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, + .use_single_read = true, + .use_single_write = true, .volatile_reg = es8326_volatile_register, + .writeable_reg = es8326_writeable_register, .cache_type = REGCACHE_RBTREE, }; @@ -877,6 +895,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ dev_dbg(comp->dev, "button pressed\n"); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); queue_delayed_work(system_wq, &es8326->button_press_work, 10); goto exit; } @@ -972,14 +992,10 @@ static int es8326_calibrate(struct snd_soc_component *component) return 0; } -static int es8326_resume(struct snd_soc_component *component) +static void es8326_init(struct snd_soc_component *component) { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - regcache_cache_only(es8326->regmap, false); - regcache_sync(es8326->regmap); - - /* reset internal clock state */ regmap_write(es8326->regmap, ES8326_RESET, 0x1f); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); @@ -1035,7 +1051,6 @@ static int es8326_resume(struct snd_soc_component *component) es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, @@ -1051,11 +1066,28 @@ static int es8326_resume(struct snd_soc_component *component) ES8326_MUTE); regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f); + regmap_write(es8326->regmap, ES8326_CLK_DIV_LRCK, 0xff); - es8326->jack_remove_retry = 0; - es8326->hp = 0; - es8326->hpl_vol = 0x03; - es8326->hpr_vol = 0x03; + msleep(200); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); +} + +static int es8326_resume(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + regcache_cache_only(es8326->regmap, false); + regcache_cache_bypass(es8326->regmap, true); + regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, ®); + regcache_cache_bypass(es8326->regmap, false); + /* reset internal clock state */ + if (reg == 0x05) + regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); + else + es8326_init(component); + + regcache_sync(es8326->regmap); es8326_irq(es8326->irq, es8326); return 0; @@ -1115,7 +1147,7 @@ static int es8326_probe(struct snd_soc_component *component) } dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); - es8326_resume(component); + es8326_init(component); return 0; } @@ -1211,6 +1243,10 @@ static int es8326_i2c_probe(struct i2c_client *i2c) } es8326->irq = i2c->irq; + es8326->jack_remove_retry = 0; + es8326->hp = 0; + es8326->hpl_vol = 0x03; + es8326->hpr_vol = 0x03; INIT_DELAYED_WORK(&es8326->jack_detect_work, es8326_jack_detect_handler); INIT_DELAYED_WORK(&es8326->button_press_work, diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c index e5fcde9ee308..6f57a3aeecc8 100644 --- a/sound/soc/codecs/framer-codec.c +++ b/sound/soc/codecs/framer-codec.c @@ -238,7 +238,7 @@ static int framer_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 framer_dai_formats[] = { +static const u64 framer_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d3abb7ce2153..74caae52e127 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -715,7 +715,7 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) * For example, * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c */ -static u64 hdmi_codec_formats = +static const u64 hdmi_codec_formats = SND_SOC_POSSIBLE_DAIFMT_NB_NF | SND_SOC_POSSIBLE_DAIFMT_NB_IF | SND_SOC_POSSIBLE_DAIFMT_IB_NF | diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 2cc7b9166e69..cb7a68c799f8 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -860,7 +860,7 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 idt821034_dai_formats[] = { +static const u64 idt821034_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c index 9df58e23d360..6217e611259f 100644 --- a/sound/soc/codecs/jz4760.c +++ b/sound/soc/codecs/jz4760.c @@ -821,7 +821,7 @@ static const u8 jz4760_codec_reg_defaults[] = { 0x1F, 0x00, 0x00, 0x00 }; -static struct regmap_config jz4760_codec_regmap_config = { +static const struct regmap_config jz4760_codec_regmap_config = { .reg_bits = 7, .val_bits = 8, diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 1d0c467ab57b..acb9eaa7ea1c 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -872,7 +872,7 @@ static const u8 jz4770_codec_reg_defaults[] = { 0x07, 0x44, 0x1F, 0x00 }; -static struct regmap_config jz4770_codec_regmap_config = { +static const struct regmap_config jz4770_codec_regmap_config = { .reg_bits = 7, .val_bits = 8, diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index da1b422250b8..6e3b8d0897dd 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -11,6 +11,9 @@ #include "lpass-macro-common.h" +static DEFINE_MUTEX(lpass_codec_mutex); +static enum lpass_codec_version lpass_codec_version; + struct lpass_macro *lpass_macro_pds_init(struct device *dev) { struct lpass_macro *l_pds; @@ -66,5 +69,25 @@ void lpass_macro_pds_exit(struct lpass_macro *pds) } EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); +void lpass_macro_set_codec_version(enum lpass_codec_version version) +{ + mutex_lock(&lpass_codec_mutex); + lpass_codec_version = version; + mutex_unlock(&lpass_codec_mutex); +} +EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); + +enum lpass_codec_version lpass_macro_get_codec_version(void) +{ + enum lpass_codec_version ver; + + mutex_lock(&lpass_codec_mutex); + ver = lpass_codec_version; + mutex_unlock(&lpass_codec_mutex); + + return ver; +} +EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); + MODULE_DESCRIPTION("Common macro driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index d98718b3dc4b..21cb30ab706d 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -18,6 +18,19 @@ enum lpass_version { LPASS_VER_11_0_0, }; +enum lpass_codec_version { + LPASS_CODEC_VERSION_UNKNOWN, + LPASS_CODEC_VERSION_1_0, + LPASS_CODEC_VERSION_1_1, + LPASS_CODEC_VERSION_1_2, + LPASS_CODEC_VERSION_2_0, + LPASS_CODEC_VERSION_2_1, + LPASS_CODEC_VERSION_2_5, + LPASS_CODEC_VERSION_2_6, + LPASS_CODEC_VERSION_2_7, + LPASS_CODEC_VERSION_2_8, +}; + struct lpass_macro { struct device *macro_pd; struct device *dcodec_pd; @@ -25,5 +38,33 @@ struct lpass_macro { struct lpass_macro *lpass_macro_pds_init(struct device *dev); void lpass_macro_pds_exit(struct lpass_macro *pds); +void lpass_macro_set_codec_version(enum lpass_codec_version version); +enum lpass_codec_version lpass_macro_get_codec_version(void); + +static inline void lpass_macro_pds_exit_action(void *pds) +{ + lpass_macro_pds_exit(pds); +} + +static inline const char *lpass_macro_get_codec_version_string(int version) +{ + switch (version) { + case LPASS_CODEC_VERSION_2_0: + return "v2.0"; + case LPASS_CODEC_VERSION_2_1: + return "v2.1"; + case LPASS_CODEC_VERSION_2_5: + return "v2.5"; + case LPASS_CODEC_VERSION_2_6: + return "v2.6"; + case LPASS_CODEC_VERSION_2_7: + return "v2.7"; + case LPASS_CODEC_VERSION_2_8: + return "v2.8"; + default: + break; + } + return "NA"; +} #endif /* __LPASS_MACRO_COMMON_H__ */ diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index f35187d69cac..ce42749660c8 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> @@ -158,7 +159,7 @@ #define CDC_RX_INTR_CTRL_LEVEL0 (0x03C0) #define CDC_RX_INTR_CTRL_BYPASS0 (0x03C8) #define CDC_RX_INTR_CTRL_SET0 (0x03D0) -#define CDC_RX_RXn_RX_PATH_CTL(n) (0x0400 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CTL(rx, n) (0x0400 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_CTL (0x0400) #define CDC_RX_PATH_RESET_EN_MASK BIT(6) #define CDC_RX_PATH_CLK_EN_MASK BIT(5) @@ -166,45 +167,47 @@ #define CDC_RX_PATH_PGA_MUTE_MASK BIT(4) #define CDC_RX_PATH_PGA_MUTE_ENABLE BIT(4) #define CDC_RX_PATH_PCM_RATE_MASK GENMASK(3, 0) -#define CDC_RX_RXn_RX_PATH_CFG0(n) (0x0404 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG0(rx, n) (0x0404 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_COMP_EN_MASK BIT(1) #define CDC_RX_RX0_RX_PATH_CFG0 (0x0404) #define CDC_RX_RXn_CLSH_EN_MASK BIT(6) #define CDC_RX_DLY_ZN_EN_MASK BIT(3) #define CDC_RX_DLY_ZN_ENABLE BIT(3) #define CDC_RX_RXn_HD2_EN_MASK BIT(2) -#define CDC_RX_RXn_RX_PATH_CFG1(n) (0x0408 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG1(rx, n) (0x0408 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_SIDETONE_EN_MASK BIT(4) #define CDC_RX_RX0_RX_PATH_CFG1 (0x0408) #define CDC_RX_RX0_HPH_L_EAR_SEL_MASK BIT(1) -#define CDC_RX_RXn_RX_PATH_CFG2(n) (0x040C + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG2(rx, n) (0x040C + rx->rxn_reg_stride * n) #define CDC_RX_RXn_HPF_CUT_FREQ_MASK GENMASK(1, 0) #define CDC_RX_RX0_RX_PATH_CFG2 (0x040C) -#define CDC_RX_RXn_RX_PATH_CFG3(n) (0x0410 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_CFG3(rx, n) (0x0410 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_CFG3 (0x0410) #define CDC_RX_DC_COEFF_SEL_MASK GENMASK(1, 0) #define CDC_RX_DC_COEFF_SEL_TWO 0x2 -#define CDC_RX_RXn_RX_VOL_CTL(n) (0x0414 + 0x80 * n) +#define CDC_RX_RXn_RX_VOL_CTL(rx, n) (0x0414 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_VOL_CTL (0x0414) -#define CDC_RX_RXn_RX_PATH_MIX_CTL(n) (0x0418 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_MIX_CTL(rx, n) (0x0418 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_MIX_PCM_RATE_MASK GENMASK(3, 0) #define CDC_RX_RXn_MIX_RESET_MASK BIT(6) #define CDC_RX_RXn_MIX_RESET BIT(6) #define CDC_RX_RXn_MIX_CLK_EN_MASK BIT(5) #define CDC_RX_RX0_RX_PATH_MIX_CTL (0x0418) #define CDC_RX_RX0_RX_PATH_MIX_CFG (0x041C) -#define CDC_RX_RXn_RX_VOL_MIX_CTL(n) (0x0420 + 0x80 * n) +#define CDC_RX_RXn_RX_VOL_MIX_CTL(rx, n) (0x0420 + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_VOL_MIX_CTL (0x0420) #define CDC_RX_RX0_RX_PATH_SEC1 (0x0424) #define CDC_RX_RX0_RX_PATH_SEC2 (0x0428) #define CDC_RX_RX0_RX_PATH_SEC3 (0x042C) +#define CDC_RX_RXn_RX_PATH_SEC3(rx, n) (0x042c + rx->rxn_reg_stride * n) #define CDC_RX_RX0_RX_PATH_SEC4 (0x0430) #define CDC_RX_RX0_RX_PATH_SEC7 (0x0434) +#define CDC_RX_RXn_RX_PATH_SEC7(rx, n) (0x0434 + rx->rxn_reg_stride * n) #define CDC_RX_DSM_OUT_DELAY_SEL_MASK GENMASK(2, 0) #define CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE 0x2 #define CDC_RX_RX0_RX_PATH_MIX_SEC0 (0x0438) #define CDC_RX_RX0_RX_PATH_MIX_SEC1 (0x043C) -#define CDC_RX_RXn_RX_PATH_DSM_CTL(n) (0x0440 + 0x80 * n) +#define CDC_RX_RXn_RX_PATH_DSM_CTL(rx, n) (0x0440 + rx->rxn_reg_stride * n) #define CDC_RX_RXn_DSM_CLK_EN_MASK BIT(0) #define CDC_RX_RX0_RX_PATH_DSM_CTL (0x0440) #define CDC_RX_RX0_RX_PATH_DSM_DATA1 (0x0444) @@ -213,6 +216,7 @@ #define CDC_RX_RX0_RX_PATH_DSM_DATA4 (0x0450) #define CDC_RX_RX0_RX_PATH_DSM_DATA5 (0x0454) #define CDC_RX_RX0_RX_PATH_DSM_DATA6 (0x0458) +/* RX offsets prior to 2.5 codec version */ #define CDC_RX_RX1_RX_PATH_CTL (0x0480) #define CDC_RX_RX1_RX_PATH_CFG0 (0x0484) #define CDC_RX_RX1_RX_PATH_CFG1 (0x0488) @@ -259,6 +263,53 @@ #define CDC_RX_RX2_RX_PATH_MIX_SEC0 (0x0544) #define CDC_RX_RX2_RX_PATH_MIX_SEC1 (0x0548) #define CDC_RX_RX2_RX_PATH_DSM_CTL (0x054C) + +/* LPASS CODEC version 2.5 rx reg offsets */ +#define CDC_2_5_RX_RX1_RX_PATH_CTL (0x04c0) +#define CDC_2_5_RX_RX1_RX_PATH_CFG0 (0x04c4) +#define CDC_2_5_RX_RX1_RX_PATH_CFG1 (0x04c8) +#define CDC_2_5_RX_RX1_RX_PATH_CFG2 (0x04cC) +#define CDC_2_5_RX_RX1_RX_PATH_CFG3 (0x04d0) +#define CDC_2_5_RX_RX1_RX_VOL_CTL (0x04d4) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_CTL (0x04d8) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_CFG (0x04dC) +#define CDC_2_5_RX_RX1_RX_VOL_MIX_CTL (0x04e0) +#define CDC_2_5_RX_RX1_RX_PATH_SEC1 (0x04e4) +#define CDC_2_5_RX_RX1_RX_PATH_SEC2 (0x04e8) +#define CDC_2_5_RX_RX1_RX_PATH_SEC3 (0x04eC) +#define CDC_2_5_RX_RX1_RX_PATH_SEC4 (0x04f0) +#define CDC_2_5_RX_RX1_RX_PATH_SEC7 (0x04f4) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0 (0x04f8) +#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1 (0x04fC) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_CTL (0x0500) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1 (0x0504) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2 (0x0508) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3 (0x050C) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4 (0x0510) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5 (0x0514) +#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6 (0x0518) + +#define CDC_2_5_RX_RX2_RX_PATH_CTL (0x0580) +#define CDC_2_5_RX_RX2_RX_PATH_CFG0 (0x0584) +#define CDC_2_5_RX_RX2_RX_PATH_CFG1 (0x0588) +#define CDC_2_5_RX_RX2_RX_PATH_CFG2 (0x058C) +#define CDC_2_5_RX_RX2_RX_PATH_CFG3 (0x0590) +#define CDC_2_5_RX_RX2_RX_VOL_CTL (0x0594) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_CTL (0x0598) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_CFG (0x059C) +#define CDC_2_5_RX_RX2_RX_VOL_MIX_CTL (0x05a0) +#define CDC_2_5_RX_RX2_RX_PATH_SEC0 (0x05a4) +#define CDC_2_5_RX_RX2_RX_PATH_SEC1 (0x05a8) +#define CDC_2_5_RX_RX2_RX_PATH_SEC2 (0x05aC) +#define CDC_2_5_RX_RX2_RX_PATH_SEC3 (0x05b0) +#define CDC_2_5_RX_RX2_RX_PATH_SEC4 (0x05b4) +#define CDC_2_5_RX_RX2_RX_PATH_SEC5 (0x05b8) +#define CDC_2_5_RX_RX2_RX_PATH_SEC6 (0x05bC) +#define CDC_2_5_RX_RX2_RX_PATH_SEC7 (0x05c0) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0 (0x05c4) +#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1 (0x05c8) +#define CDC_2_5_RX_RX2_RX_PATH_DSM_CTL (0x05cC) + #define CDC_RX_IDLE_DETECT_PATH_CTL (0x0780) #define CDC_RX_IDLE_DETECT_CFG0 (0x0784) #define CDC_RX_IDLE_DETECT_CFG1 (0x0788) @@ -463,12 +514,6 @@ static const struct comp_coeff_val comp_coeff_table[HPH_MODE_MAX][COMP_MAX_COEFF }, }; -struct rx_macro_reg_mask_val { - u16 reg; - u8 mask; - u8 val; -}; - enum { INTERP_HPHL, INTERP_HPHR, @@ -598,6 +643,8 @@ struct rx_macro { int rx_mclk_users; int clsh_users; int rx_mclk_cnt; + enum lpass_codec_version codec_version; + int rxn_reg_stride; bool is_ear_mode_on; bool hph_pwr_mode; bool hph_hd2_mode; @@ -759,6 +806,8 @@ static SOC_ENUM_SINGLE_DECL(rx_int0_dem_inp_enum, CDC_RX_RX0_RX_PATH_CFG1, 0, rx_int_dem_inp_mux_text); static SOC_ENUM_SINGLE_DECL(rx_int1_dem_inp_enum, CDC_RX_RX1_RX_PATH_CFG1, 0, rx_int_dem_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_2_5_int1_dem_inp_enum, CDC_2_5_RX_RX1_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text); static SOC_ENUM_SINGLE_DECL(rx_macro_rx0_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); static SOC_ENUM_SINGLE_DECL(rx_macro_rx1_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); @@ -976,49 +1025,6 @@ static const struct reg_default rx_defaults[] = { { CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55 }, { CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55 }, { CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55 }, - { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, - { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, - { CDC_RX_RX1_RX_PATH_CFG1, 0x64 }, - { CDC_RX_RX1_RX_PATH_CFG2, 0x8F }, - { CDC_RX_RX1_RX_PATH_CFG3, 0x00 }, - { CDC_RX_RX1_RX_VOL_CTL, 0x00 }, - { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, - { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, - { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC1, 0x08 }, - { CDC_RX_RX1_RX_PATH_SEC2, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC3, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC4, 0x00 }, - { CDC_RX_RX1_RX_PATH_SEC7, 0x00 }, - { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, - { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, - { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, - { CDC_RX_RX2_RX_PATH_CTL, 0x04 }, - { CDC_RX_RX2_RX_PATH_CFG0, 0x00 }, - { CDC_RX_RX2_RX_PATH_CFG1, 0x64 }, - { CDC_RX_RX2_RX_PATH_CFG2, 0x8F }, - { CDC_RX_RX2_RX_PATH_CFG3, 0x00 }, - { CDC_RX_RX2_RX_VOL_CTL, 0x00 }, - { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, - { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, - { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC0, 0x04 }, - { CDC_RX_RX2_RX_PATH_SEC1, 0x08 }, - { CDC_RX_RX2_RX_PATH_SEC2, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC3, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC4, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC5, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC6, 0x00 }, - { CDC_RX_RX2_RX_PATH_SEC7, 0x00 }, - { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, - { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, - { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, { CDC_RX_IDLE_DETECT_PATH_CTL, 0x00 }, { CDC_RX_IDLE_DETECT_CFG0, 0x07 }, { CDC_RX_IDLE_DETECT_CFG1, 0x3C }, @@ -1121,6 +1127,99 @@ static const struct reg_default rx_defaults[] = { { CDC_RX_DSD1_CFG2, 0x96 }, }; +static const struct reg_default rx_2_5_defaults[] = { + { CDC_2_5_RX_RX1_RX_PATH_CTL, 0x04 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG0, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG1, 0x64 }, + { CDC_2_5_RX_RX1_RX_PATH_CFG2, 0x8F }, + { CDC_2_5_RX_RX1_RX_PATH_CFG3, 0x00 }, + { CDC_2_5_RX_RX1_RX_VOL_CTL, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, + { CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC1, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC2, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC3, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC4, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_SEC7, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_2_5_RX_RX2_RX_PATH_CTL, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG0, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG1, 0x64 }, + { CDC_2_5_RX_RX2_RX_PATH_CFG2, 0x8F }, + { CDC_2_5_RX_RX2_RX_PATH_CFG3, 0x00 }, + { CDC_2_5_RX_RX2_RX_VOL_CTL, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, + { CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC0, 0x04 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC1, 0x08 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC2, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC3, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC4, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC5, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC6, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_SEC7, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_2_5_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, +}; + +static const struct reg_default rx_pre_2_5_defaults[] = { + { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX1_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX1_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX1_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX1_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX1_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_RX_RX2_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX2_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX2_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX2_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX2_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC0, 0x04 }, + { CDC_RX_RX2_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX2_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC5, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC6, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, + +}; + static bool rx_is_wronly_register(struct device *dev, unsigned int reg) { @@ -1175,8 +1274,114 @@ static bool rx_is_volatile_register(struct device *dev, unsigned int reg) return false; } +static bool rx_pre_2_5_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_RX_RX1_RX_PATH_CTL: + case CDC_RX_RX1_RX_PATH_CFG0: + case CDC_RX_RX1_RX_PATH_CFG1: + case CDC_RX_RX1_RX_PATH_CFG2: + case CDC_RX_RX1_RX_PATH_CFG3: + case CDC_RX_RX1_RX_VOL_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CFG: + case CDC_RX_RX1_RX_VOL_MIX_CTL: + case CDC_RX_RX1_RX_PATH_SEC1: + case CDC_RX_RX1_RX_PATH_SEC2: + case CDC_RX_RX1_RX_PATH_SEC3: + case CDC_RX_RX1_RX_PATH_SEC4: + case CDC_RX_RX1_RX_PATH_SEC7: + case CDC_RX_RX1_RX_PATH_MIX_SEC0: + case CDC_RX_RX1_RX_PATH_MIX_SEC1: + case CDC_RX_RX1_RX_PATH_DSM_CTL: + case CDC_RX_RX1_RX_PATH_DSM_DATA1: + case CDC_RX_RX1_RX_PATH_DSM_DATA2: + case CDC_RX_RX1_RX_PATH_DSM_DATA3: + case CDC_RX_RX1_RX_PATH_DSM_DATA4: + case CDC_RX_RX1_RX_PATH_DSM_DATA5: + case CDC_RX_RX1_RX_PATH_DSM_DATA6: + case CDC_RX_RX2_RX_PATH_CTL: + case CDC_RX_RX2_RX_PATH_CFG0: + case CDC_RX_RX2_RX_PATH_CFG1: + case CDC_RX_RX2_RX_PATH_CFG2: + case CDC_RX_RX2_RX_PATH_CFG3: + case CDC_RX_RX2_RX_VOL_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CFG: + case CDC_RX_RX2_RX_VOL_MIX_CTL: + case CDC_RX_RX2_RX_PATH_SEC0: + case CDC_RX_RX2_RX_PATH_SEC1: + case CDC_RX_RX2_RX_PATH_SEC2: + case CDC_RX_RX2_RX_PATH_SEC3: + case CDC_RX_RX2_RX_PATH_SEC4: + case CDC_RX_RX2_RX_PATH_SEC5: + case CDC_RX_RX2_RX_PATH_SEC6: + case CDC_RX_RX2_RX_PATH_SEC7: + case CDC_RX_RX2_RX_PATH_MIX_SEC0: + case CDC_RX_RX2_RX_PATH_MIX_SEC1: + case CDC_RX_RX2_RX_PATH_DSM_CTL: + return true; + } + + return false; +} + +static bool rx_2_5_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_RX_RX1_RX_PATH_CTL: + case CDC_2_5_RX_RX1_RX_PATH_CFG0: + case CDC_2_5_RX_RX1_RX_PATH_CFG1: + case CDC_2_5_RX_RX1_RX_PATH_CFG2: + case CDC_2_5_RX_RX1_RX_PATH_CFG3: + case CDC_2_5_RX_RX1_RX_VOL_CTL: + case CDC_2_5_RX_RX1_RX_PATH_MIX_CTL: + case CDC_2_5_RX_RX1_RX_PATH_MIX_CFG: + case CDC_2_5_RX_RX1_RX_VOL_MIX_CTL: + case CDC_2_5_RX_RX1_RX_PATH_SEC1: + case CDC_2_5_RX_RX1_RX_PATH_SEC2: + case CDC_2_5_RX_RX1_RX_PATH_SEC3: + case CDC_2_5_RX_RX1_RX_PATH_SEC4: + case CDC_2_5_RX_RX1_RX_PATH_SEC7: + case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0: + case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1: + case CDC_2_5_RX_RX1_RX_PATH_DSM_CTL: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5: + case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6: + case CDC_2_5_RX_RX2_RX_PATH_CTL: + case CDC_2_5_RX_RX2_RX_PATH_CFG0: + case CDC_2_5_RX_RX2_RX_PATH_CFG1: + case CDC_2_5_RX_RX2_RX_PATH_CFG2: + case CDC_2_5_RX_RX2_RX_PATH_CFG3: + case CDC_2_5_RX_RX2_RX_VOL_CTL: + case CDC_2_5_RX_RX2_RX_PATH_MIX_CTL: + case CDC_2_5_RX_RX2_RX_PATH_MIX_CFG: + case CDC_2_5_RX_RX2_RX_VOL_MIX_CTL: + case CDC_2_5_RX_RX2_RX_PATH_SEC0: + case CDC_2_5_RX_RX2_RX_PATH_SEC1: + case CDC_2_5_RX_RX2_RX_PATH_SEC2: + case CDC_2_5_RX_RX2_RX_PATH_SEC3: + case CDC_2_5_RX_RX2_RX_PATH_SEC4: + case CDC_2_5_RX_RX2_RX_PATH_SEC5: + case CDC_2_5_RX_RX2_RX_PATH_SEC6: + case CDC_2_5_RX_RX2_RX_PATH_SEC7: + case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0: + case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1: + case CDC_2_5_RX_RX2_RX_PATH_DSM_CTL: + return true; + } + + return false; +} + static bool rx_is_rw_register(struct device *dev, unsigned int reg) { + struct rx_macro *rx = dev_get_drvdata(dev); + switch (reg) { case CDC_RX_TOP_TOP_CFG0: case CDC_RX_TOP_SWR_CTRL: @@ -1306,49 +1511,6 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) case CDC_RX_RX0_RX_PATH_DSM_DATA4: case CDC_RX_RX0_RX_PATH_DSM_DATA5: case CDC_RX_RX0_RX_PATH_DSM_DATA6: - case CDC_RX_RX1_RX_PATH_CTL: - case CDC_RX_RX1_RX_PATH_CFG0: - case CDC_RX_RX1_RX_PATH_CFG1: - case CDC_RX_RX1_RX_PATH_CFG2: - case CDC_RX_RX1_RX_PATH_CFG3: - case CDC_RX_RX1_RX_VOL_CTL: - case CDC_RX_RX1_RX_PATH_MIX_CTL: - case CDC_RX_RX1_RX_PATH_MIX_CFG: - case CDC_RX_RX1_RX_VOL_MIX_CTL: - case CDC_RX_RX1_RX_PATH_SEC1: - case CDC_RX_RX1_RX_PATH_SEC2: - case CDC_RX_RX1_RX_PATH_SEC3: - case CDC_RX_RX1_RX_PATH_SEC4: - case CDC_RX_RX1_RX_PATH_SEC7: - case CDC_RX_RX1_RX_PATH_MIX_SEC0: - case CDC_RX_RX1_RX_PATH_MIX_SEC1: - case CDC_RX_RX1_RX_PATH_DSM_CTL: - case CDC_RX_RX1_RX_PATH_DSM_DATA1: - case CDC_RX_RX1_RX_PATH_DSM_DATA2: - case CDC_RX_RX1_RX_PATH_DSM_DATA3: - case CDC_RX_RX1_RX_PATH_DSM_DATA4: - case CDC_RX_RX1_RX_PATH_DSM_DATA5: - case CDC_RX_RX1_RX_PATH_DSM_DATA6: - case CDC_RX_RX2_RX_PATH_CTL: - case CDC_RX_RX2_RX_PATH_CFG0: - case CDC_RX_RX2_RX_PATH_CFG1: - case CDC_RX_RX2_RX_PATH_CFG2: - case CDC_RX_RX2_RX_PATH_CFG3: - case CDC_RX_RX2_RX_VOL_CTL: - case CDC_RX_RX2_RX_PATH_MIX_CTL: - case CDC_RX_RX2_RX_PATH_MIX_CFG: - case CDC_RX_RX2_RX_VOL_MIX_CTL: - case CDC_RX_RX2_RX_PATH_SEC0: - case CDC_RX_RX2_RX_PATH_SEC1: - case CDC_RX_RX2_RX_PATH_SEC2: - case CDC_RX_RX2_RX_PATH_SEC3: - case CDC_RX_RX2_RX_PATH_SEC4: - case CDC_RX_RX2_RX_PATH_SEC5: - case CDC_RX_RX2_RX_PATH_SEC6: - case CDC_RX_RX2_RX_PATH_SEC7: - case CDC_RX_RX2_RX_PATH_MIX_SEC0: - case CDC_RX_RX2_RX_PATH_MIX_SEC1: - case CDC_RX_RX2_RX_PATH_DSM_CTL: case CDC_RX_IDLE_DETECT_PATH_CTL: case CDC_RX_IDLE_DETECT_CFG0: case CDC_RX_IDLE_DETECT_CFG1: @@ -1435,6 +1597,22 @@ static bool rx_is_rw_register(struct device *dev, unsigned int reg) return true; } + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + return rx_pre_2_5_is_rw_register(dev, reg); + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + return rx_2_5_is_rw_register(dev, reg); + default: + break; + } + return false; } @@ -1491,8 +1669,6 @@ static const struct regmap_config rx_regmap_config = { .val_bits = 32, /* 8 but with 32 bit read/write */ .reg_stride = 4, .cache_type = REGCACHE_FLAT, - .reg_defaults = rx_defaults, - .num_reg_defaults = ARRAY_SIZE(rx_defaults), .max_register = RX_MAX_OFFSET, .writeable_reg = rx_is_writeable_register, .volatile_reg = rx_is_volatile_register, @@ -1504,16 +1680,17 @@ static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, { 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 rx_macro *rx = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned short look_ahead_dly_reg; unsigned int val; val = ucontrol->value.enumerated.item[0]; - if (e->reg == CDC_RX_RX0_RX_PATH_CFG1) - look_ahead_dly_reg = CDC_RX_RX0_RX_PATH_CFG0; - else if (e->reg == CDC_RX_RX1_RX_PATH_CFG1) - look_ahead_dly_reg = CDC_RX_RX1_RX_PATH_CFG0; + if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 0)) + look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0); + else if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 1)) + look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1); /* Set Look Ahead Delay */ if (val) @@ -1534,6 +1711,10 @@ static const struct snd_kcontrol_new rx_int1_dem_inp_mux = SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_int1_dem_inp_enum, snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); +static const struct snd_kcontrol_new rx_2_5_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_2_5_int1_dem_inp_enum, + snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); + static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, int rate_reg_val, u32 sample_rate) { @@ -1567,7 +1748,7 @@ static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { - int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(j); + int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(rx, j); /* sample_rate is in Hz */ snd_soc_component_update_bits(component, int_fs_reg, CDC_RX_PATH_PCM_RATE_MASK, @@ -1600,7 +1781,7 @@ static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, CDC_RX_INTX_2_SEL_MASK); if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { - int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); + int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j); snd_soc_component_update_bits(component, int_fs_reg, CDC_RX_RXn_MIX_PCM_RATE_MASK, rate_reg_val); @@ -1654,7 +1835,7 @@ static int rx_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int rx_macro_get_channel_map(struct snd_soc_dai *dai, +static int rx_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -1719,6 +1900,7 @@ static int rx_macro_get_channel_map(struct snd_soc_dai *dai, static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); uint16_t j, reg, mix_reg, dsm_reg; u16 int_mux_cfg0, int_mux_cfg1; u8 int_mux_cfg0_val, int_mux_cfg1_val; @@ -1729,9 +1911,9 @@ static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) case RX_MACRO_AIF3_PB: case RX_MACRO_AIF4_PB: for (j = 0; j < INTERP_MAX; j++) { - reg = CDC_RX_RXn_RX_PATH_CTL(j); - mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); - dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j); + reg = CDC_RX_RXn_RX_PATH_CTL(rx, j); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, j); if (mute) { snd_soc_component_update_bits(component, reg, @@ -1748,7 +1930,7 @@ static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) } if (j == INTERP_AUX) - dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 2); int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8; int_mux_cfg1 = int_mux_cfg0 + 4; @@ -1956,10 +2138,11 @@ static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); u16 gain_reg, reg; - reg = CDC_RX_RXn_RX_PATH_CTL(w->shift); - gain_reg = CDC_RX_RXn_RX_VOL_CTL(w->shift); + reg = CDC_RX_RXn_RX_PATH_CTL(rx, w->shift); + gain_reg = CDC_RX_RXn_RX_VOL_CTL(rx, w->shift); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1991,7 +2174,7 @@ static int rx_macro_config_compander(struct snd_soc_component *component, if (comp == INTERP_AUX) return 0; - pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(comp)) & 0x0F; + pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(rx, comp)) & 0x0F; if (pcm_rate < 0x06) val = 0x03; else if (pcm_rate < 0x08) @@ -2002,11 +2185,11 @@ static int rx_macro_config_compander(struct snd_soc_component *component, val = 0x00; if (SND_SOC_DAPM_EVENT_ON(event)) - snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp), CDC_RX_DC_COEFF_SEL_MASK, val); if (SND_SOC_DAPM_EVENT_OFF(event)) - snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, comp), CDC_RX_DC_COEFF_SEL_MASK, 0x3); if (!rx->comp_enabled[comp]) return 0; @@ -2019,14 +2202,14 @@ static int rx_macro_config_compander(struct snd_soc_component *component, CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x1); snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x0); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp), CDC_RX_RXn_COMP_EN_MASK, 0x1); } if (SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_HALT_MASK, 0x1); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(rx, comp), CDC_RX_RXn_COMP_EN_MASK, 0x0); snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), CDC_RX_COMPANDERn_CLK_EN_MASK, 0x0); @@ -2125,13 +2308,13 @@ static int rx_macro_config_aux_hpf(struct snd_soc_component *component, /* Update Aux HPF control */ if (!rx->is_aux_hpf_on) snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x00); + CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x00); } if (SND_SOC_DAPM_EVENT_OFF(event)) { /* Reset to default (HPF=ON) */ snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x04); + CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x04); } return 0; @@ -2183,7 +2366,7 @@ static int rx_macro_config_classh(struct snd_soc_component *component, CDC_RX_CLSH_DECAY_CTRL, CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 0), CDC_RX_RXn_CLSH_EN_MASK, 0x1); break; case INTERP_HPHR: @@ -2199,15 +2382,15 @@ static int rx_macro_config_classh(struct snd_soc_component *component, CDC_RX_CLSH_DECAY_CTRL, CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); snd_soc_component_write_field(component, - CDC_RX_RX1_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 1), CDC_RX_RXn_CLSH_EN_MASK, 0x1); break; case INTERP_AUX: snd_soc_component_update_bits(component, - CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 2), CDC_RX_RX2_DLY_Z_EN_MASK, 1); snd_soc_component_write_field(component, - CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RXn_RX_PATH_CFG0(rx, 2), CDC_RX_RX2_CLSH_EN_MASK, 1); break; } @@ -2218,16 +2401,17 @@ static int rx_macro_config_classh(struct snd_soc_component *component, static void rx_macro_hd2_control(struct snd_soc_component *component, u16 interp_idx, int event) { + struct rx_macro *rx = snd_soc_component_get_drvdata(component); u16 hd2_scale_reg, hd2_enable_reg; switch (interp_idx) { case INTERP_HPHL: - hd2_scale_reg = CDC_RX_RX0_RX_PATH_SEC3; - hd2_enable_reg = CDC_RX_RX0_RX_PATH_CFG0; + hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 0); + hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0); break; case INTERP_HPHR: - hd2_scale_reg = CDC_RX_RX1_RX_PATH_SEC3; - hd2_enable_reg = CDC_RX_RX1_RX_PATH_CFG0; + hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 1); + hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1); break; } @@ -2482,7 +2666,7 @@ static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, if (interp_idx == INTERP_HPHL) { if (rx->is_ear_mode_on) snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RXn_RX_PATH_CFG1(rx, 0), CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x1); else snd_soc_component_write_field(component, @@ -2499,7 +2683,7 @@ static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_component_write_field(component, - CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RXn_RX_PATH_CFG1(rx, 0), CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x0); snd_soc_component_update_bits(component, hph_lut_bypass_reg, CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 0); @@ -2516,11 +2700,12 @@ static int rx_macro_enable_interp_clk(struct snd_soc_component *component, u16 main_reg, dsm_reg, rx_cfg2_reg; struct rx_macro *rx = snd_soc_component_get_drvdata(component); - main_reg = CDC_RX_RXn_RX_PATH_CTL(interp_idx); - dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(interp_idx); + main_reg = CDC_RX_RXn_RX_PATH_CTL(rx, interp_idx); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, interp_idx); if (interp_idx == INTERP_AUX) - dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; - rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(interp_idx); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 2); + + rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(rx, interp_idx); if (SND_SOC_DAPM_EVENT_ON(event)) { if (rx->main_clk_users[interp_idx] == 0) { @@ -2587,10 +2772,11 @@ static int rx_macro_enable_mix_path(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 rx_macro *rx = snd_soc_component_get_drvdata(component); u16 gain_reg, mix_reg; - gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(w->shift); - mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(w->shift); + gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(rx, w->shift); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, w->shift); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -2621,17 +2807,18 @@ static int rx_macro_enable_rx_path_clk(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 rx_macro *rx = snd_soc_component_get_drvdata(component); switch (event) { case SND_SOC_DAPM_PRE_PMU: rx_macro_enable_interp_clk(component, event, w->shift); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift), CDC_RX_RXn_SIDETONE_EN_MASK, 1); - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(rx, w->shift), CDC_RX_PATH_CLK_EN_MASK, 1); break; case SND_SOC_DAPM_POST_PMD: - snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(rx, w->shift), CDC_RX_RXn_SIDETONE_EN_MASK, 0); rx_macro_enable_interp_clk(component, event, w->shift); break; @@ -2801,20 +2988,34 @@ static int rx_macro_iir_filter_info(struct snd_kcontrol *kcontrol, return 0; } -static const struct snd_kcontrol_new rx_macro_snd_controls[] = { - SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, - -84, 40, digital_gain), +static const struct snd_kcontrol_new rx_macro_def_snd_controls[] = { SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_RX_RX1_RX_VOL_CTL, -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_RX_RX2_RX_VOL_CTL, -84, 40, digital_gain), - SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL, - -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_RX_RX1_RX_VOL_MIX_CTL, -84, 40, digital_gain), SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_RX_RX2_RX_VOL_MIX_CTL, -84, 40, digital_gain), +}; + +static const struct snd_kcontrol_new rx_macro_2_5_snd_controls[] = { + + SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_2_5_RX_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_2_5_RX_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), +}; +static const struct snd_kcontrol_new rx_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0, rx_macro_get_compander, rx_macro_set_compander), SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0, @@ -2932,6 +3133,16 @@ static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w, return 0; } +static const struct snd_soc_dapm_widget rx_macro_2_5_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_2_5_int1_dem_inp_mux), +}; + +static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), +}; + static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), @@ -3003,8 +3214,6 @@ static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, &rx_int0_dem_inp_mux), - SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, - &rx_int1_dem_inp_mux), SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, &rx_int0_2_mux, rx_macro_enable_mix_path, @@ -3399,32 +3608,65 @@ static const struct snd_soc_dapm_route rx_audio_map[] = { static int rx_macro_component_probe(struct snd_soc_component *component) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct rx_macro *rx = snd_soc_component_get_drvdata(component); + const struct snd_soc_dapm_widget *widgets; + const struct snd_kcontrol_new *controls; + unsigned int num_controls, num_widgets; + int ret; snd_soc_component_init_regmap(component, rx->regmap); - snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 0), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 1), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_SEC7, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 2), CDC_RX_DSM_OUT_DELAY_SEL_MASK, CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); - snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 0), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); - snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 1), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); - snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_CFG3, + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 2), CDC_RX_DC_COEFF_SEL_MASK, CDC_RX_DC_COEFF_SEL_TWO); + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + controls = rx_macro_def_snd_controls; + num_controls = ARRAY_SIZE(rx_macro_def_snd_controls); + widgets = rx_macro_def_dapm_widgets; + num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets); + break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + controls = rx_macro_2_5_snd_controls; + num_controls = ARRAY_SIZE(rx_macro_2_5_snd_controls); + widgets = rx_macro_2_5_dapm_widgets; + num_widgets = ARRAY_SIZE(rx_macro_2_5_dapm_widgets); + break; + default: + return -EINVAL; + } + rx->component = component; - return 0; + ret = snd_soc_add_component_controls(component, controls, num_controls); + if (ret) + return ret; + + return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); } static int swclk_gate_enable(struct clk_hw *hw) @@ -3527,7 +3769,7 @@ static int rx_macro_probe(struct platform_device *pdev) kernel_ulong_t flags; struct rx_macro *rx; void __iomem *base; - int ret; + int ret, def_count; flags = (kernel_ulong_t)device_get_match_data(dev); @@ -3561,17 +3803,62 @@ static int rx_macro_probe(struct platform_device *pdev) if (IS_ERR(rx->pds)) return PTR_ERR(rx->pds); + ret = devm_add_action_or_reset(dev, lpass_macro_pds_exit_action, rx->pds); + if (ret) + return ret; + base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto err; + if (IS_ERR(base)) + return PTR_ERR(base); + + rx->codec_version = lpass_macro_get_codec_version(); + struct reg_default *reg_defaults __free(kfree) = NULL; + + switch (rx->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + rx->rxn_reg_stride = 0x80; + def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults); + reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); + memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], + rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults)); + break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + rx->rxn_reg_stride = 0xc0; + def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults); + reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], rx_defaults, sizeof(rx_defaults)); + memcpy(®_defaults[ARRAY_SIZE(rx_defaults)], + rx_2_5_defaults, sizeof(rx_2_5_defaults)); + break; + default: + dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version); + return -EINVAL; } - rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); - if (IS_ERR(rx->regmap)) { - ret = PTR_ERR(rx->regmap); - goto err; - } + struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config, + sizeof(*reg_config), + GFP_KERNEL); + if (!reg_config) + return -ENOMEM; + + reg_config->reg_defaults = reg_defaults; + reg_config->num_reg_defaults = def_count; + + rx->regmap = devm_regmap_init_mmio(dev, base, reg_config); + if (IS_ERR(rx->regmap)) + return PTR_ERR(rx->regmap); dev_set_drvdata(dev, rx); @@ -3583,7 +3870,7 @@ static int rx_macro_probe(struct platform_device *pdev) ret = clk_prepare_enable(rx->macro); if (ret) - goto err; + return ret; ret = clk_prepare_enable(rx->dcodec); if (ret) @@ -3641,8 +3928,6 @@ err_mclk: clk_disable_unprepare(rx->dcodec); err_dcodec: clk_disable_unprepare(rx->macro); -err: - lpass_macro_pds_exit(rx->pds); return ret; } @@ -3656,8 +3941,6 @@ static void rx_macro_remove(struct platform_device *pdev) clk_disable_unprepare(rx->fsgen); clk_disable_unprepare(rx->macro); clk_disable_unprepare(rx->dcodec); - - lpass_macro_pds_exit(rx->pds); } static const struct of_device_id rx_macro_dt_match[] = { diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index c98b0b747a92..209c12ec16dd 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1167,7 +1167,7 @@ static int tx_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int tx_macro_get_channel_map(struct snd_soc_dai *dai, +static int tx_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 6eceeff10bf6..b852cc7ffad9 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -892,7 +892,7 @@ static int va_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int va_macro_get_channel_map(struct snd_soc_dai *dai, +static int va_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -1461,6 +1461,33 @@ undefined_rate: return dmic_sample_rate; } +static void va_macro_set_lpass_codec_version(struct va_macro *va) +{ + int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0; + int version = LPASS_CODEC_VERSION_UNKNOWN; + + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &core_id_0); + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &core_id_1); + regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_2, &core_id_2); + + if ((core_id_0 == 0x01) && (core_id_1 == 0x0F)) + version = LPASS_CODEC_VERSION_2_0; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0E)) + version = LPASS_CODEC_VERSION_2_1; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x50 || core_id_2 == 0x51)) + version = LPASS_CODEC_VERSION_2_5; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x60 || core_id_2 == 0x61)) + version = LPASS_CODEC_VERSION_2_6; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x70 || core_id_2 == 0x71)) + version = LPASS_CODEC_VERSION_2_7; + if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81)) + version = LPASS_CODEC_VERSION_2_8; + + lpass_macro_set_codec_version(version); + + dev_dbg(va->dev, "LPASS Codec Version %s\n", lpass_macro_get_codec_version_string(version)); +} + static int va_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1554,6 +1581,8 @@ static int va_macro_probe(struct platform_device *pdev) goto err_npl; } + va_macro_set_lpass_codec_version(va); + if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 6ce309980cd1..73a588289408 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/init.h> #include <linux/io.h> @@ -44,11 +45,7 @@ #define CDC_WSA_TOP_I2S_CLK (0x00A4) #define CDC_WSA_TOP_I2S_RESET (0x00A8) #define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100) -#define CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(2, 0) -#define CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(5, 3) #define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104) -#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0) -#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3) #define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108) #define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C) #define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110) @@ -173,22 +170,7 @@ #define CDC_WSA_COMPANDER0_CTL5 (0x0594) #define CDC_WSA_COMPANDER0_CTL6 (0x0598) #define CDC_WSA_COMPANDER0_CTL7 (0x059C) -#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) -#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) -#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) -#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) -#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) -#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) -#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) -#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) -#define CDC_WSA_SOFTCLIP0_CRC (0x0600) -#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) -#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) -#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) -#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) -#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) -#define CDC_WSA_SOFTCLIP1_CRC (0x0640) -#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) +/* CDC_WSA_COMPANDER1_CTLx and CDC_WSA_SOFTCLIPx differ per LPASS codec versions */ #define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680) #define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0) #define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0) @@ -217,6 +199,65 @@ #define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760) #define WSA_MAX_OFFSET (0x0760) +/* LPASS codec version <=2.4 register offsets */ +#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) +#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) +#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) +#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) +#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) +#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) +#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) +#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) +#define CDC_WSA_SOFTCLIP0_CRC (0x0600) +#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) +#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP1_CRC (0x0640) +#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) + +/* LPASS codec version >=2.5 register offsets */ +#define CDC_WSA_TOP_FS_UNGATE (0x00AC) +#define CDC_WSA_TOP_GRP_SEL (0x00B0) +#define CDC_WSA_TOP_FS_UNGATE2 (0x00DC) +#define CDC_2_5_WSA_COMPANDER0_CTL8 (0x05A0) +#define CDC_2_5_WSA_COMPANDER0_CTL9 (0x05A4) +#define CDC_2_5_WSA_COMPANDER0_CTL10 (0x05A8) +#define CDC_2_5_WSA_COMPANDER0_CTL11 (0x05AC) +#define CDC_2_5_WSA_COMPANDER0_CTL12 (0x05B0) +#define CDC_2_5_WSA_COMPANDER0_CTL13 (0x05B4) +#define CDC_2_5_WSA_COMPANDER0_CTL14 (0x05B8) +#define CDC_2_5_WSA_COMPANDER0_CTL15 (0x05BC) +#define CDC_2_5_WSA_COMPANDER0_CTL16 (0x05C0) +#define CDC_2_5_WSA_COMPANDER0_CTL17 (0x05C4) +#define CDC_2_5_WSA_COMPANDER0_CTL18 (0x05C8) +#define CDC_2_5_WSA_COMPANDER0_CTL19 (0x05CC) +#define CDC_2_5_WSA_COMPANDER1_CTL0 (0x05E0) +#define CDC_2_5_WSA_COMPANDER1_CTL1 (0x05E4) +#define CDC_2_5_WSA_COMPANDER1_CTL2 (0x05E8) +#define CDC_2_5_WSA_COMPANDER1_CTL3 (0x05EC) +#define CDC_2_5_WSA_COMPANDER1_CTL4 (0x05F0) +#define CDC_2_5_WSA_COMPANDER1_CTL5 (0x05F4) +#define CDC_2_5_WSA_COMPANDER1_CTL6 (0x05F8) +#define CDC_2_5_WSA_COMPANDER1_CTL7 (0x05FC) +#define CDC_2_5_WSA_COMPANDER1_CTL8 (0x0600) +#define CDC_2_5_WSA_COMPANDER1_CTL9 (0x0604) +#define CDC_2_5_WSA_COMPANDER1_CTL10 (0x0608) +#define CDC_2_5_WSA_COMPANDER1_CTL11 (0x060C) +#define CDC_2_5_WSA_COMPANDER1_CTL12 (0x0610) +#define CDC_2_5_WSA_COMPANDER1_CTL13 (0x0614) +#define CDC_2_5_WSA_COMPANDER1_CTL14 (0x0618) +#define CDC_2_5_WSA_COMPANDER1_CTL15 (0x061C) +#define CDC_2_5_WSA_COMPANDER1_CTL16 (0x0620) +#define CDC_2_5_WSA_COMPANDER1_CTL17 (0x0624) +#define CDC_2_5_WSA_COMPANDER1_CTL18 (0x0628) +#define CDC_2_5_WSA_COMPANDER1_CTL19 (0x062C) +#define CDC_2_5_WSA_SOFTCLIP0_CRC (0x0640) +#define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644) +#define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660) +#define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664) + #define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) @@ -235,11 +276,8 @@ #define NUM_INTERPOLATORS 2 #define WSA_NUM_CLKS_MAX 5 #define WSA_MACRO_MCLK_FREQ 19200000 -#define WSA_MACRO_MUX_INP_MASK2 0x38 #define WSA_MACRO_MUX_CFG_OFFSET 0x8 #define WSA_MACRO_MUX_CFG1_OFFSET 0x4 -#define WSA_MACRO_RX_COMP_OFFSET 0x40 -#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 #define WSA_MACRO_RX_PATH_OFFSET 0x80 #define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 #define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C @@ -335,12 +373,34 @@ enum { WSA_MACRO_MAX_DAIS, }; +/** + * struct wsa_reg_layout - Register layout differences + * @rx_intx_1_mix_inp0_sel_mask: register mask for RX_INTX_1_MIX_INP0_SEL_MASK + * @rx_intx_1_mix_inp1_sel_mask: register mask for RX_INTX_1_MIX_INP1_SEL_MASK + * @rx_intx_1_mix_inp2_sel_mask: register mask for RX_INTX_1_MIX_INP2_SEL_MASK + * @rx_intx_2_sel_mask: register mask for RX_INTX_2_SEL_MASK + * @compander1_reg_offset: offset between compander registers (compander1 - compander0) + * @softclip0_reg_base: base address of softclip0 register + * @softclip1_reg_offset: offset between compander registers (softclip1 - softclip0) + */ +struct wsa_reg_layout { + unsigned int rx_intx_1_mix_inp0_sel_mask; + unsigned int rx_intx_1_mix_inp1_sel_mask; + unsigned int rx_intx_1_mix_inp2_sel_mask; + unsigned int rx_intx_2_sel_mask; + unsigned int compander1_reg_offset; + unsigned int softclip0_reg_base; + unsigned int softclip1_reg_offset; +}; + struct wsa_macro { struct device *dev; int comp_enabled[WSA_MACRO_COMP_MAX]; int ec_hq[WSA_MACRO_RX1 + 1]; u16 prim_int_users[WSA_MACRO_RX1 + 1]; u16 wsa_mclk_users; + enum lpass_codec_version codec_version; + const struct wsa_reg_layout *reg_layout; unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; int rx_port_value[WSA_MACRO_RX_MAX]; @@ -359,16 +419,44 @@ struct wsa_macro { }; #define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) +static const struct wsa_reg_layout wsa_codec_v2_1 = { + .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0), + .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3), + .rx_intx_1_mix_inp2_sel_mask = GENMASK(5, 3), + .rx_intx_2_sel_mask = GENMASK(2, 0), + .compander1_reg_offset = 0x40, + .softclip0_reg_base = 0x600, + .softclip1_reg_offset = 0x40, +}; + +static const struct wsa_reg_layout wsa_codec_v2_5 = { + .rx_intx_1_mix_inp0_sel_mask = GENMASK(3, 0), + .rx_intx_1_mix_inp1_sel_mask = GENMASK(7, 4), + .rx_intx_1_mix_inp2_sel_mask = GENMASK(7, 4), + .rx_intx_2_sel_mask = GENMASK(3, 0), + .compander1_reg_offset = 0x60, + .softclip0_reg_base = 0x640, + .softclip1_reg_offset = 0x20, +}; + static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); -static const char *const rx_text[] = { +static const char *const rx_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" }; -static const char *const rx_mix_text[] = { +static const char *const rx_text_v2_5[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8", "DEC0", "DEC1" +}; + +static const char *const rx_mix_text_v2_1[] = { "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" }; +static const char *const rx_mix_text_v2_5[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8" +}; + static const char *const rx_mix_ec_text[] = { "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" }; @@ -390,68 +478,124 @@ static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, wsa_macro_ear_spkr_pa_gain_text); /* RX INT0 */ -static const struct soc_enum rx0_prim_inp0_chain_enum = +static const struct soc_enum rx0_prim_inp0_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, - 0, 7, rx_text); + 0, 7, rx_text_v2_1); -static const struct soc_enum rx0_prim_inp1_chain_enum = +static const struct soc_enum rx0_prim_inp1_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); + +static const struct soc_enum rx0_prim_inp2_chain_enum_v2_1 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 3, 7, rx_text_v2_1); -static const struct soc_enum rx0_prim_inp2_chain_enum = +static const struct soc_enum rx0_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, - 3, 7, rx_text); + 0, 5, rx_mix_text_v2_1); + +static const struct soc_enum rx0_prim_inp0_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 12, rx_text_v2_5); + +static const struct soc_enum rx0_prim_inp1_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 4, 12, rx_text_v2_5); -static const struct soc_enum rx0_mix_chain_enum = +static const struct soc_enum rx0_prim_inp2_chain_enum_v2_5 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, - 0, 5, rx_mix_text); + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx0_mix_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 10, rx_mix_text_v2_5); static const struct soc_enum rx0_sidetone_mix_enum = SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); -static const struct snd_kcontrol_new rx0_prim_inp0_mux = - SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx0_mix_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1); -static const struct snd_kcontrol_new rx0_prim_inp1_mux = - SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_5); -static const struct snd_kcontrol_new rx0_prim_inp2_mux = - SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_5); -static const struct snd_kcontrol_new rx0_mix_mux = - SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); +static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx0_mix_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_5); static const struct snd_kcontrol_new rx0_sidetone_mix_mux = SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); /* RX INT1 */ -static const struct soc_enum rx1_prim_inp0_chain_enum = +static const struct soc_enum rx1_prim_inp0_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, - 0, 7, rx_text); + 0, 7, rx_text_v2_1); -static const struct soc_enum rx1_prim_inp1_chain_enum = +static const struct soc_enum rx1_prim_inp1_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx1_prim_inp2_chain_enum = +static const struct soc_enum rx1_prim_inp2_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, - 3, 7, rx_text); + 3, 7, rx_text_v2_1); -static const struct soc_enum rx1_mix_chain_enum = +static const struct soc_enum rx1_mix_chain_enum_v2_1 = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, - 0, 5, rx_mix_text); + 0, 5, rx_mix_text_v2_1); -static const struct snd_kcontrol_new rx1_prim_inp0_mux = - SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); +static const struct soc_enum rx1_prim_inp0_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 12, rx_text_v2_5); -static const struct snd_kcontrol_new rx1_prim_inp1_mux = - SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); +static const struct soc_enum rx1_prim_inp1_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 4, 12, rx_text_v2_5); -static const struct snd_kcontrol_new rx1_prim_inp2_mux = - SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); +static const struct soc_enum rx1_prim_inp2_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 4, 12, rx_text_v2_5); + +static const struct soc_enum rx1_mix_chain_enum_v2_5 = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 10, rx_mix_text_v2_5); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx1_mix_mux_v2_1 = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_5); -static const struct snd_kcontrol_new rx1_mix_mux = - SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); +static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_5); + +static const struct snd_kcontrol_new rx1_mix_mux_v2_5 = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_5); static const struct soc_enum rx_mix_ec0_enum = SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, @@ -490,14 +634,6 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, - { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, - { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, - { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, { CDC_WSA_INTR_CTRL_CFG, 0x00}, { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, @@ -562,18 +698,6 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_COMPANDER0_CTL5, 0x00}, { CDC_WSA_COMPANDER0_CTL6, 0x01}, { CDC_WSA_COMPANDER0_CTL7, 0x28}, - { CDC_WSA_COMPANDER1_CTL0, 0x60}, - { CDC_WSA_COMPANDER1_CTL1, 0xDB}, - { CDC_WSA_COMPANDER1_CTL2, 0xFF}, - { CDC_WSA_COMPANDER1_CTL3, 0x35}, - { CDC_WSA_COMPANDER1_CTL4, 0xFF}, - { CDC_WSA_COMPANDER1_CTL5, 0x00}, - { CDC_WSA_COMPANDER1_CTL6, 0x01}, - { CDC_WSA_COMPANDER1_CTL7, 0x28}, - { CDC_WSA_SOFTCLIP0_CRC, 0x00}, - { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, - { CDC_WSA_SOFTCLIP1_CRC, 0x00}, - { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, @@ -598,6 +722,79 @@ static const struct reg_default wsa_defaults[] = { { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, }; +static const struct reg_default wsa_defaults_v2_1[] = { + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, +}; + +static const struct reg_default wsa_defaults_v2_5[] = { + { CDC_WSA_TOP_FS_UNGATE, 0xFF}, + { CDC_WSA_TOP_GRP_SEL, 0x08}, + { CDC_WSA_TOP_FS_UNGATE2, 0x1F}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x04}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x02}, + { CDC_2_5_WSA_COMPANDER0_CTL8, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL9, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL10, 0x06}, + { CDC_2_5_WSA_COMPANDER0_CTL11, 0x12}, + { CDC_2_5_WSA_COMPANDER0_CTL12, 0x1E}, + { CDC_2_5_WSA_COMPANDER0_CTL13, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL14, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL15, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL16, 0x00}, + { CDC_2_5_WSA_COMPANDER0_CTL17, 0x24}, + { CDC_2_5_WSA_COMPANDER0_CTL18, 0x2A}, + { CDC_2_5_WSA_COMPANDER0_CTL19, 0x16}, + { CDC_2_5_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_2_5_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_2_5_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_2_5_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_2_5_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_2_5_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_2_5_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_2_5_WSA_COMPANDER1_CTL8, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL9, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL10, 0x06}, + { CDC_2_5_WSA_COMPANDER1_CTL11, 0x12}, + { CDC_2_5_WSA_COMPANDER1_CTL12, 0x1E}, + { CDC_2_5_WSA_COMPANDER1_CTL13, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL14, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL15, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL16, 0x00}, + { CDC_2_5_WSA_COMPANDER1_CTL17, 0x24}, + { CDC_2_5_WSA_COMPANDER1_CTL18, 0x2A}, + { CDC_2_5_WSA_COMPANDER1_CTL19, 0x16}, + { CDC_2_5_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_2_5_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, +}; + static bool wsa_is_wronly_register(struct device *dev, unsigned int reg) { @@ -611,8 +808,77 @@ static bool wsa_is_wronly_register(struct device *dev, return false; } +static bool wsa_is_rw_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL0: + case CDC_WSA_COMPANDER1_CTL1: + case CDC_WSA_COMPANDER1_CTL2: + case CDC_WSA_COMPANDER1_CTL3: + case CDC_WSA_COMPANDER1_CTL4: + case CDC_WSA_COMPANDER1_CTL5: + case CDC_WSA_COMPANDER1_CTL7: + case CDC_WSA_SOFTCLIP0_CRC: + case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_WSA_SOFTCLIP1_CRC: + case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + return true; + } + + return false; +} + +static bool wsa_is_rw_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_TOP_FS_UNGATE: + case CDC_WSA_TOP_GRP_SEL: + case CDC_WSA_TOP_FS_UNGATE2: + case CDC_2_5_WSA_COMPANDER0_CTL8: + case CDC_2_5_WSA_COMPANDER0_CTL9: + case CDC_2_5_WSA_COMPANDER0_CTL10: + case CDC_2_5_WSA_COMPANDER0_CTL11: + case CDC_2_5_WSA_COMPANDER0_CTL12: + case CDC_2_5_WSA_COMPANDER0_CTL13: + case CDC_2_5_WSA_COMPANDER0_CTL14: + case CDC_2_5_WSA_COMPANDER0_CTL15: + case CDC_2_5_WSA_COMPANDER0_CTL16: + case CDC_2_5_WSA_COMPANDER0_CTL17: + case CDC_2_5_WSA_COMPANDER0_CTL18: + case CDC_2_5_WSA_COMPANDER0_CTL19: + case CDC_2_5_WSA_COMPANDER1_CTL0: + case CDC_2_5_WSA_COMPANDER1_CTL1: + case CDC_2_5_WSA_COMPANDER1_CTL2: + case CDC_2_5_WSA_COMPANDER1_CTL3: + case CDC_2_5_WSA_COMPANDER1_CTL4: + case CDC_2_5_WSA_COMPANDER1_CTL5: + case CDC_2_5_WSA_COMPANDER1_CTL7: + case CDC_2_5_WSA_COMPANDER1_CTL8: + case CDC_2_5_WSA_COMPANDER1_CTL9: + case CDC_2_5_WSA_COMPANDER1_CTL10: + case CDC_2_5_WSA_COMPANDER1_CTL11: + case CDC_2_5_WSA_COMPANDER1_CTL12: + case CDC_2_5_WSA_COMPANDER1_CTL13: + case CDC_2_5_WSA_COMPANDER1_CTL14: + case CDC_2_5_WSA_COMPANDER1_CTL15: + case CDC_2_5_WSA_COMPANDER1_CTL16: + case CDC_2_5_WSA_COMPANDER1_CTL17: + case CDC_2_5_WSA_COMPANDER1_CTL18: + case CDC_2_5_WSA_COMPANDER1_CTL19: + case CDC_2_5_WSA_SOFTCLIP0_CRC: + case CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_2_5_WSA_SOFTCLIP1_CRC: + case CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + return true; + } + + return false; +} + static bool wsa_is_rw_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + switch (reg) { case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL: case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL: @@ -702,17 +968,6 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) case CDC_WSA_COMPANDER0_CTL4: case CDC_WSA_COMPANDER0_CTL5: case CDC_WSA_COMPANDER0_CTL7: - case CDC_WSA_COMPANDER1_CTL0: - case CDC_WSA_COMPANDER1_CTL1: - case CDC_WSA_COMPANDER1_CTL2: - case CDC_WSA_COMPANDER1_CTL3: - case CDC_WSA_COMPANDER1_CTL4: - case CDC_WSA_COMPANDER1_CTL5: - case CDC_WSA_COMPANDER1_CTL7: - case CDC_WSA_SOFTCLIP0_CRC: - case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: - case CDC_WSA_SOFTCLIP1_CRC: - case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL: case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0: case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL: @@ -728,7 +983,10 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) return true; } - return false; + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_rw_register_v2_5(dev, reg); + + return wsa_is_rw_register_v2_1(dev, reg); } static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) @@ -742,8 +1000,30 @@ static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) return ret; } +static bool wsa_is_readable_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL6: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + +static bool wsa_is_readable_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_WSA_COMPANDER1_CTL6: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + static bool wsa_is_readable_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + switch (reg) { case CDC_WSA_INTR_CTRL_CLR_COMMIT: case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: @@ -751,7 +1031,6 @@ static bool wsa_is_readable_register(struct device *dev, unsigned int reg) case CDC_WSA_INTR_CTRL_PIN1_STATUS0: case CDC_WSA_INTR_CTRL_PIN2_STATUS0: case CDC_WSA_COMPANDER0_CTL6: - case CDC_WSA_COMPANDER1_CTL6: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: @@ -765,17 +1044,41 @@ static bool wsa_is_readable_register(struct device *dev, unsigned int reg) return true; } - return wsa_is_rw_register(dev, reg); + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_readable_register_v2_5(dev, reg); + + return wsa_is_readable_register_v2_1(dev, reg); +} + +static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_COMPANDER1_CTL6: + return true; + } + + return false; +} + +static bool wsa_is_volatile_register_v2_5(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_2_5_WSA_COMPANDER1_CTL6: + return true; + } + + return false; } static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) { + struct wsa_macro *wsa = dev_get_drvdata(dev); + /* Update volatile list for rx/tx macros */ switch (reg) { case CDC_WSA_INTR_CTRL_PIN1_STATUS0: case CDC_WSA_INTR_CTRL_PIN2_STATUS0: case CDC_WSA_COMPANDER0_CTL6: - case CDC_WSA_COMPANDER1_CTL6: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: @@ -788,7 +1091,11 @@ static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: return true; } - return false; + + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + return wsa_is_volatile_register_v2_5(dev, reg); + + return wsa_is_volatile_register_v2_1(dev, reg); } static const struct regmap_config wsa_regmap_config = { @@ -797,8 +1104,7 @@ static const struct regmap_config wsa_regmap_config = { .val_bits = 32, /* 8 but with 32 bit read/write */ .reg_stride = 4, .cache_type = REGCACHE_FLAT, - .reg_defaults = wsa_defaults, - .num_reg_defaults = ARRAY_SIZE(wsa_defaults), + /* .reg_defaults and .num_reg_defaults set in probe() */ .max_register = WSA_MAX_OFFSET, .writeable_reg = wsa_is_writeable_register, .volatile_reg = wsa_is_volatile_register, @@ -872,11 +1178,11 @@ static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, for (j = 0; j < NUM_INTERPOLATORS; j++) { int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); - inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask); + inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, + wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask); inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp2_sel_mask); if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || @@ -917,7 +1223,7 @@ static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; for (j = 0; j < NUM_INTERPOLATORS; j++) { int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_2_SEL_MASK); + wsa->reg_layout->rx_intx_2_sel_mask); if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL + @@ -992,7 +1298,7 @@ static int wsa_macro_hw_params(struct snd_pcm_substream *substream, return 0; } -static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, +static int wsa_macro_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -1300,7 +1606,7 @@ static int wsa_macro_config_compander(struct snd_soc_component *component, return 0; comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 + - (comp * WSA_MACRO_RX_COMP_OFFSET); + (comp * wsa->reg_layout->compander1_reg_offset); rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 + (comp * WSA_MACRO_RX_PATH_OFFSET); @@ -1346,8 +1652,8 @@ static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, int path, bool enable) { - u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC + - (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u16 softclip_clk_reg = wsa->reg_layout->softclip0_reg_base + + (path * wsa->reg_layout->softclip1_reg_offset); u8 softclip_mux_mask = (1 << path); u8 softclip_mux_value = (1 << path); @@ -1392,7 +1698,7 @@ static int wsa_macro_config_softclip(struct snd_soc_component *component, return 0; softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + - (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + (softclip_path * wsa->reg_layout->softclip1_reg_offset); if (SND_SOC_DAPM_EVENT_ON(event)) { /* Enable Softclip clock and mux */ @@ -1417,6 +1723,7 @@ static int wsa_macro_config_softclip(struct snd_soc_component *component, static bool wsa_macro_adie_lb(struct snd_soc_component *component, int interp_idx) { + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); u16 int_mux_cfg0, int_mux_cfg1; u8 int_n_inp0, int_n_inp1, int_n_inp2; @@ -1424,19 +1731,19 @@ static bool wsa_macro_adie_lb(struct snd_soc_component *component, int_mux_cfg1 = int_mux_cfg0 + 4; int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask); if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || int_n_inp0 == INTn_1_INP_SEL_DEC1) return true; int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0, - CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask); if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || int_n_inp1 == INTn_1_INP_SEL_DEC1) return true; int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1, - CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + wsa->reg_layout->rx_intx_1_mix_inp2_sel_mask); if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || int_n_inp2 == INTn_1_INP_SEL_DEC1) return true; @@ -2074,19 +2381,6 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux), - SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux), - SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux), - SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, - 0, &rx0_mix_mux, wsa_macro_enable_mix_path, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux), - SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux), - SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux), - SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, - 0, &rx1_mix_mux, wsa_macro_enable_mix_path, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0, wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0, @@ -2137,6 +2431,36 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_1[] = { + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_1), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, + 0, &rx0_mix_mux_v2_1, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_1), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_1), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, + 0, &rx1_mix_mux_v2_1, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_5[] = { + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_5), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, + 0, &rx0_mix_mux_v2_5, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_5), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_5), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, + 0, &rx1_mix_mux_v2_5, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + static const struct snd_soc_dapm_route wsa_audio_map[] = { /* VI Feedback */ {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, @@ -2282,7 +2606,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) static int wsa_macro_component_probe(struct snd_soc_component *comp) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp); struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp); + const struct snd_soc_dapm_widget *widgets; + unsigned int num_widgets; snd_soc_component_init_regmap(comp, wsa->regmap); @@ -2299,7 +2626,27 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp) wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1); - return 0; + switch (wsa->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + widgets = wsa_macro_dapm_widgets_v2_1; + num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1); + break; + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + widgets = wsa_macro_dapm_widgets_v2_5; + num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5); + break; + default: + return -EINVAL; + } + + return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); } static int swclk_gate_enable(struct clk_hw *hw) @@ -2382,7 +2729,7 @@ static int wsa_macro_probe(struct platform_device *pdev) struct wsa_macro *wsa; kernel_ulong_t flags; void __iomem *base; - int ret; + int ret, def_count; flags = (kernel_ulong_t)device_get_match_data(dev); @@ -2416,7 +2763,56 @@ static int wsa_macro_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config); + wsa->codec_version = lpass_macro_get_codec_version(); + struct reg_default *reg_defaults __free(kfree) = NULL; + + switch (wsa->codec_version) { + case LPASS_CODEC_VERSION_1_0: + case LPASS_CODEC_VERSION_1_1: + case LPASS_CODEC_VERSION_1_2: + case LPASS_CODEC_VERSION_2_0: + case LPASS_CODEC_VERSION_2_1: + wsa->reg_layout = &wsa_codec_v2_1; + def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1); + reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults), + GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); + memcpy(®_defaults[ARRAY_SIZE(wsa_defaults)], + wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1)); + break; + + case LPASS_CODEC_VERSION_2_5: + case LPASS_CODEC_VERSION_2_6: + case LPASS_CODEC_VERSION_2_7: + case LPASS_CODEC_VERSION_2_8: + wsa->reg_layout = &wsa_codec_v2_5; + def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5); + reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults), + GFP_KERNEL); + if (!reg_defaults) + return -ENOMEM; + memcpy(®_defaults[0], wsa_defaults, sizeof(wsa_defaults)); + memcpy(®_defaults[ARRAY_SIZE(wsa_defaults)], + wsa_defaults_v2_5, sizeof(wsa_defaults_v2_5)); + break; + + default: + dev_err(dev, "Unsupported Codec version (%d)\n", wsa->codec_version); + return -EINVAL; + } + + struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config, + sizeof(*reg_config), + GFP_KERNEL); + if (!reg_config) + return -ENOMEM; + + reg_config->reg_defaults = reg_defaults; + reg_config->num_reg_defaults = def_count; + + wsa->regmap = devm_regmap_init_mmio(dev, base, reg_config); if (IS_ERR(wsa->regmap)) return PTR_ERR(wsa->regmap); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 8b56ee550c09..8b0645c63462 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1318,6 +1318,7 @@ static int max98088_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + int ret; switch (level) { case SND_SOC_BIAS_ON: @@ -1333,10 +1334,13 @@ static int max98088_set_bias_level(struct snd_soc_component *component, */ if (!IS_ERR(max98088->mclk)) { if (snd_soc_component_get_bias_level(component) == - SND_SOC_BIAS_ON) + SND_SOC_BIAS_ON) { clk_disable_unprepare(max98088->mclk); - else - clk_prepare_enable(max98088->mclk); + } else { + ret = clk_prepare_enable(max98088->mclk); + if (ret) + return ret; + } } break; diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 57fa2db1e148..1bae253618fd 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -13,7 +13,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/time.h> diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 93412b966b33..6b6a7ece4cec 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -220,8 +220,10 @@ static int max98504_set_tdm_slot(struct snd_soc_dai *dai, return 0; } static int max98504_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai); struct regmap *map = max98504->regmap; diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index 0284e29c11d3..9247b90d1b99 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -96,7 +96,7 @@ struct mt6358_priv { int wov_enabled; - unsigned int dmic_one_wire_mode; + int dmic_one_wire_mode; }; int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, @@ -577,6 +577,39 @@ static int mt6358_put_wov(struct snd_kcontrol *kcontrol, return 0; } +static int mt6358_dmic_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->dmic_one_wire_mode; + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 0; +} + +static int mt6358_dmic_mode_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + + if (enabled < 0 || enabled > 1) + return -EINVAL; + + if (priv->dmic_one_wire_mode != enabled) { + priv->dmic_one_wire_mode = enabled; + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 1; + } + dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode); + + return 0; +} + static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); @@ -599,6 +632,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = { SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0, mt6358_get_wov, mt6358_put_wov), + + SOC_SINGLE_BOOL_EXT("Dmic Mode Switch", 0, + mt6358_dmic_mode_get, mt6358_dmic_mode_set), }; /* MUX */ diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index e6909e64dfa3..e1cbaf8a944d 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -14,6 +14,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> @@ -612,20 +613,6 @@ static const struct snd_soc_dapm_route nau8822_dapm_routes[] = { {"Right DAC", NULL, "Digital Loopback"}, }; -static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct snd_soc_component *component = dai->component; - struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); - - nau8822->div_id = clk_id; - nau8822->sysclk = freq; - dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, - clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); - - return 0; -} - static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs, struct nau8822_pll *pll_param) { @@ -782,6 +769,35 @@ static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source, return 0; } +static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + unsigned long mclk_freq; + + nau8822->div_id = clk_id; + nau8822->sysclk = freq; + + if (nau8822->mclk) { + mclk_freq = clk_get_rate(nau8822->mclk); + if (mclk_freq != freq) { + int ret = nau8822_set_pll(dai, NAU8822_CLK_MCLK, + NAU8822_CLK_MCLK, mclk_freq, freq); + if (ret) { + dev_err(component->dev, "Failed to set PLL\n"); + return ret; + } + nau8822->div_id = NAU8822_CLK_PLL; + } + } + + dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, + nau8822->div_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; @@ -848,7 +864,7 @@ static int nau8822_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); - int val_len = 0, val_rate = 0; + int div = 0, val_len = 0, val_rate = 0; unsigned int ctrl_val, bclk_fs, bclk_div; /* make BCLK and LRC divide configuration if the codec as master. */ @@ -915,8 +931,10 @@ static int nau8822_hw_params(struct snd_pcm_substream *substream, /* If the master clock is from MCLK, provide the runtime FS for driver * to get the master clock prescaler configuration. */ - if (nau8822->div_id == NAU8822_CLK_MCLK) - nau8822_config_clkdiv(dai, 0, params_rate(params)); + if (nau8822->div_id != NAU8822_CLK_MCLK) + div = nau8822->pll.mclk_scaler; + + nau8822_config_clkdiv(dai, div, params_rate(params)); return 0; } @@ -940,15 +958,34 @@ static int nau8822_mute(struct snd_soc_dai *dai, int mute, int direction) static int nau8822_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + if (nau8822->mclk && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) { + int ret = clk_prepare_enable(nau8822->mclk); + + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", ret); + return ret; + } + } + snd_soc_component_update_bits(component, NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K); break; case SND_SOC_BIAS_STANDBY: + if (nau8822->mclk && + snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_OFF) + clk_disable_unprepare(nau8822->mclk); + snd_soc_component_update_bits(component, NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_IOBUF_EN | NAU8822_ABIAS_EN, @@ -1125,6 +1162,11 @@ static int nau8822_i2c_probe(struct i2c_client *i2c) } i2c_set_clientdata(i2c, nau8822); + nau8822->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(nau8822->mclk)) + return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk), + "Error getting mclk\n"); + nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config); if (IS_ERR(nau8822->regmap)) { ret = PTR_ERR(nau8822->regmap); diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index 6ecd46e45923..13fe0a091e9e 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -215,6 +215,7 @@ struct nau8822_pll { struct nau8822 { struct device *dev; struct regmap *regmap; + struct clk *mclk; struct nau8822_pll pll; int sysclk; int div_id; diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index f92b95b21cae..12540397fd4d 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -506,6 +506,7 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, struct regmap *regmap = nau8824->regmap; unsigned int value; bool clk_fll, error; + int ret; if (SND_SOC_DAPM_EVENT_OFF(event)) { dev_dbg(nau8824->dev, "system clock control : POWER OFF\n"); @@ -520,8 +521,15 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, } else { nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); } + + clk_disable_unprepare(nau8824->mclk); } else { dev_dbg(nau8824->dev, "system clock control : POWER ON\n"); + + ret = clk_prepare_enable(nau8824->mclk); + if (ret) + return ret; + /* Check the clock source setting is proper or not * no matter the source is from FLL or MCLK. */ @@ -563,16 +571,21 @@ static int dmic_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); int src; + unsigned int freq; + + freq = clk_get_rate(nau8824->mclk); + if (!freq) + freq = nau8824->fs * 256; /* The DMIC clock is gotten from system clock (256fs) divided by * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or * less than 3.072 MHz. */ for (src = 0; src < 5; src++) { - if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK) + if (freq / (0x1 << src) <= DMIC_CLK) break; } - dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256); + dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, freq); regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT)); @@ -1871,6 +1884,10 @@ static int nau8824_read_device_properties(struct device *dev, if (ret) nau8824->jack_eject_debounce = 1; + nau8824->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(nau8824->mclk)) + return PTR_ERR(nau8824->mclk); + return 0; } diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h index 5fcfc43dfc85..d8e19515133c 100644 --- a/sound/soc/codecs/nau8824.h +++ b/sound/soc/codecs/nau8824.h @@ -434,6 +434,7 @@ struct nau8824 { struct snd_soc_jack *jack; struct work_struct jdet_work; struct semaphore jd_sem; + struct clk *mclk; int fs; int irq; int resume_lock; diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 9d6431338fb7..fac0617ab95b 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -11,7 +11,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -563,7 +562,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return 0; } -static u64 pcm3168a_dai_formats[] = { +static const u64 pcm3168a_dai_formats[] = { /* * Select below from Sound Card, not here * SND_SOC_DAIFMT_CBC_CFC diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 4be476a280e1..92bcf5179779 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -39,6 +39,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { "tas5754", }, { "tas5756", }, { } @@ -51,6 +52,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { .compatible = "ti,tas5754", }, { .compatible = "ti,tas5756", }, { } diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 4d29e7196380..6629b862f47d 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -36,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -45,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 86e126783a1d..6641e7c1ddf4 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -18,6 +18,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_irq.h> +#include <linux/of_address.h> #include <linux/regmap.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -57,12 +58,6 @@ static const char *const pcmdev_ctrl_name[] = { "%s i2c%d Dev%d Ch%d Fine Volume", }; -static const char *const pcmdev_ctrl_name_with_prefix[] = { - "%s Dev%d Ch%d Ana Volume", - "%s Dev%d Ch%d Digi Volume", - "%s Dev%d Ch%d Fine Volume", -}; - static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = { { .shift = 1, @@ -1365,10 +1360,7 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev, name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id; - if (comp->name_prefix) - ctrl_name = pcmdev_ctrl_name_with_prefix[name_id]; - else - ctrl_name = pcmdev_ctrl_name[name_id]; + ctrl_name = pcmdev_ctrl_name[name_id]; for (chn = 1; chn <= nr_chn; chn++) { name = devm_kzalloc(pcm_dev->dev, @@ -1377,13 +1369,9 @@ static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev, ret = -ENOMEM; goto out; } - if (comp->name_prefix) - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - ctrl_name, comp->name_prefix, dev_no, chn); - else - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - ctrl_name, pcm_dev->upper_dev_name, adap->nr, - dev_no, chn); + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + ctrl_name, pcm_dev->upper_dev_name, adap->nr, + dev_no, chn); pcmdev_controls[mix_index].tlv.p = pcmdev_gain_ctl_info[id][ctl_id].gain; pcmdev_ctrl = devm_kmemdup(pcm_dev->dev, @@ -1437,13 +1425,8 @@ static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev) if (!name) return -ENOMEM; - if (comp->name_prefix) - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s Profile id", comp->name_prefix); - else - scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s i2c%d Profile id", pcm_dev->upper_dev_name, - adap->nr); + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr); pcmdev_ctrl->name = name; pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; pcmdev_ctrl->info = pcmdevice_info_profile; @@ -2081,16 +2064,10 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) struct device_node *np; unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES]; int ret = 0, i = 0, ndev = 0; -#ifdef CONFIG_OF - const __be32 *reg, *reg_end; - int len, sw, aw; -#endif pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); - if (!pcm_dev) { - ret = -ENOMEM; - goto out; - } + if (!pcm_dev) + return -ENOMEM; pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; @@ -2120,27 +2097,19 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, pcm_dev); mutex_init(&pcm_dev->codec_lock); np = pcm_dev->dev->of_node; -#ifdef CONFIG_OF - aw = of_n_addr_cells(np); - sw = of_n_size_cells(np); - if (sw == 0) { - reg = (const __be32 *)of_get_property(np, - "reg", &len); - reg_end = reg + len/sizeof(*reg); - ndev = 0; - do { - dev_addrs[ndev] = of_read_number(reg, aw); - reg += aw; - ndev++; - } while (reg < reg_end); + + if (IS_ENABLED(CONFIG_OF)) { + u64 addr; + + for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) { + if (of_property_read_reg(np, i, &addr, NULL)) + break; + dev_addrs[ndev++] = addr; + } } else { ndev = 1; dev_addrs[0] = i2c->addr; } -#else - ndev = 1; - dev_addrs[0] = i2c->addr; -#endif pcm_dev->irq_info.gpio = of_irq_get(np, 0); for (i = 0; i < ndev; i++) diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 5dec69be0acb..76ee7e3f4d9b 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -814,7 +814,7 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream, &peb2466_sample_bits_constr); } -static u64 peb2466_dai_formats[] = { +static const u64 peb2466_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index d4da98469f8b..5fea600bc3a4 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -10,7 +10,6 @@ #include <linux/mfd/rk808.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <sound/core.h> diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c new file mode 100644 index 000000000000..83b29b441be9 --- /dev/null +++ b/sound/soc/codecs/rt1318.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1318.c -- RT1318 ALSA SoC audio amplifier driver +// Author: Jack Yu <jack.yu@realtek.com> +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// + +#include <linux/acpi.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt1318.h> + +#include "rt1318.h" + +static struct reg_sequence init_list[] = { + { 0x0000C000, 0x01}, + { 0x0000F20D, 0x00}, + { 0x0000F212, 0x3E}, + { 0x0000C001, 0x02}, + { 0x0000C003, 0x22}, + { 0x0000C004, 0x44}, + { 0x0000C005, 0x44}, + { 0x0000C007, 0x64}, + { 0x0000C00E, 0xE7}, + { 0x0000F223, 0x7F}, + { 0x0000F224, 0xDB}, + { 0x0000F225, 0xEE}, + { 0x0000F226, 0x3F}, + { 0x0000F227, 0x0F}, + { 0x0000F21A, 0x78}, + { 0x0000F242, 0x3C}, + { 0x0000C120, 0x40}, + { 0x0000C125, 0x03}, + { 0x0000C321, 0x0A}, + { 0x0000C200, 0xD8}, + { 0x0000C201, 0x27}, + { 0x0000C202, 0x0F}, + { 0x0000C400, 0x0E}, + { 0x0000C401, 0x43}, + { 0x0000C402, 0xE0}, + { 0x0000C403, 0x00}, + { 0x0000C404, 0x4C}, + { 0x0000C406, 0x40}, + { 0x0000C407, 0x02}, + { 0x0000C408, 0x3F}, + { 0x0000C300, 0x01}, + { 0x0000C125, 0x03}, + { 0x0000DF00, 0x10}, + { 0x0000F20B, 0x2A}, + { 0x0000DF5F, 0x01}, + { 0x0000DF60, 0xA7}, + { 0x0000C203, 0x84}, + { 0x0000C206, 0x78}, + { 0x0000F10A, 0x09}, + { 0x0000F10B, 0x4C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x03}, + { 0x0000F109, 0xE0}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x04}, + { 0x0000F109, 0x65}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x02}, + { 0x0000F109, 0x30}, + { 0x0000F10B, 0x5C}, + { 0x0000E706, 0x0F}, + { 0x0000E707, 0x30}, + { 0x0000E806, 0x0F}, + { 0x0000E807, 0x30}, + { 0x0000CE04, 0x03}, + { 0x0000CE05, 0x5F}, + { 0x0000CE06, 0xA2}, + { 0x0000CE07, 0x6B}, + { 0x0000CF04, 0x03}, + { 0x0000CF05, 0x5F}, + { 0x0000CF06, 0xA2}, + { 0x0000CF07, 0x6B}, + { 0x0000CE60, 0xE3}, + { 0x0000C130, 0x51}, + { 0x0000E000, 0xA8}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x04}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x41001888, 0x00}, + { 0x0000C121, 0x0B}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x00}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x0000F800, 0x20}, + { 0x0000CA00, 0x80}, + { 0x0000CA10, 0x00}, + { 0x0000CA02, 0x78}, + { 0x0000CA12, 0x78}, + { 0x0000ED00, 0x90}, + { 0x0000E604, 0x00}, + { 0x0000DB00, 0x0C}, + { 0x0000DD00, 0x0C}, + { 0x0000DC19, 0x00}, + { 0x0000DC1A, 0x6A}, + { 0x0000DC1B, 0xAA}, + { 0x0000DC1C, 0xAB}, + { 0x0000DC1D, 0x00}, + { 0x0000DC1E, 0x16}, + { 0x0000DC1F, 0xDB}, + { 0x0000DC20, 0x6D}, + { 0x0000DE19, 0x00}, + { 0x0000DE1A, 0x6A}, + { 0x0000DE1B, 0xAA}, + { 0x0000DE1C, 0xAB}, + { 0x0000DE1D, 0x00}, + { 0x0000DE1E, 0x16}, + { 0x0000DE1F, 0xDB}, + { 0x0000DE20, 0x6D}, + { 0x0000DB32, 0x00}, + { 0x0000DD32, 0x00}, + { 0x0000DB33, 0x0A}, + { 0x0000DD33, 0x0A}, + { 0x0000DB34, 0x1A}, + { 0x0000DD34, 0x1A}, + { 0x0000DB15, 0xEF}, + { 0x0000DD15, 0xEF}, + { 0x0000DB17, 0xEF}, + { 0x0000DD17, 0xEF}, + { 0x0000DB94, 0x70}, + { 0x0000DD94, 0x70}, + { 0x0000DB19, 0x40}, + { 0x0000DD19, 0x40}, + { 0x0000DB12, 0xC0}, + { 0x0000DD12, 0xC0}, + { 0x0000DB00, 0x4C}, + { 0x0000DB04, 0x05}, + { 0x0000DB05, 0x03}, + { 0x0000DD04, 0x05}, + { 0x0000DD05, 0x03}, + { 0x0000DBBB, 0x09}, + { 0x0000DBBC, 0x30}, + { 0x0000DBBD, 0xF0}, + { 0x0000DBBE, 0xF1}, + { 0x0000DDBB, 0x09}, + { 0x0000DDBC, 0x30}, + { 0x0000DDBD, 0xF0}, + { 0x0000DDBE, 0xF1}, + { 0x0000DB01, 0x79}, + { 0x0000DD01, 0x79}, + { 0x0000DB08, 0x40}, + { 0x0000DD08, 0x40}, + { 0x0000DC52, 0xEF}, + { 0x0000DE52, 0xEF}, + { 0x0000DB00, 0xCC}, + { 0x0000CC2C, 0x00}, + { 0x0000CC2D, 0x2A}, + { 0x0000CC2E, 0x83}, + { 0x0000CC2F, 0xA8}, + { 0x0000CD2C, 0x00}, + { 0x0000CD2D, 0x2A}, + { 0x0000CD2E, 0x83}, + { 0x0000CD2F, 0xA8}, + { 0x0000CC24, 0x00}, + { 0x0000CC25, 0x51}, + { 0x0000CC26, 0xEB}, + { 0x0000CC27, 0x85}, + { 0x0000CD24, 0x00}, + { 0x0000CD25, 0x51}, + { 0x0000CD26, 0xEB}, + { 0x0000CD27, 0x85}, + { 0x0000CC20, 0x00}, + { 0x0000CC21, 0x00}, + { 0x0000CC22, 0x43}, + { 0x0000CD20, 0x00}, + { 0x0000CD21, 0x00}, + { 0x0000CD22, 0x43}, + { 0x0000CC16, 0x0F}, + { 0x0000CC17, 0x00}, + { 0x0000CD16, 0x0F}, + { 0x0000CD17, 0x00}, + { 0x0000CC29, 0x5D}, + { 0x0000CC2A, 0xC0}, + { 0x0000CD29, 0x5D}, + { 0x0000CD2A, 0xC0}, + { 0x0000CC31, 0x20}, + { 0x0000CC32, 0x00}, + { 0x0000CC33, 0x00}, + { 0x0000CC34, 0x00}, + { 0x0000CD31, 0x20}, + { 0x0000CD32, 0x00}, + { 0x0000CD33, 0x00}, + { 0x0000CD34, 0x00}, + { 0x0000CC36, 0x79}, + { 0x0000CC37, 0x99}, + { 0x0000CC38, 0x99}, + { 0x0000CC39, 0x99}, + { 0x0000CD36, 0x79}, + { 0x0000CD37, 0x99}, + { 0x0000CD38, 0x99}, + { 0x0000CD39, 0x99}, + { 0x0000CC09, 0x00}, + { 0x0000CC0A, 0x07}, + { 0x0000CC0B, 0x5F}, + { 0x0000CC0C, 0x6F}, + { 0x0000CD09, 0x00}, + { 0x0000CD0A, 0x07}, + { 0x0000CD0B, 0x5F}, + { 0x0000CD0C, 0x6F}, + { 0x0000CC0E, 0x00}, + { 0x0000CC0F, 0x03}, + { 0x0000CC10, 0xAF}, + { 0x0000CC11, 0xB7}, + { 0x0000CD0E, 0x00}, + { 0x0000CD0F, 0x03}, + { 0x0000CD10, 0xAF}, + { 0x0000CD11, 0xB7}, + { 0x0000CCD6, 0x00}, + { 0x0000CCD7, 0x03}, + { 0x0000CDD6, 0x00}, + { 0x0000CDD7, 0x03}, + { 0x0000CCD8, 0x00}, + { 0x0000CCD9, 0x03}, + { 0x0000CDD8, 0x00}, + { 0x0000CDD9, 0x03}, + { 0x0000CCDA, 0x00}, + { 0x0000CCDB, 0x03}, + { 0x0000CDDA, 0x00}, + { 0x0000CDDB, 0x03}, + { 0x0000C320, 0x20}, + { 0x0000C203, 0x9C}, +}; +#define rt1318_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt1318_reg[] = { + { 0xc000, 0x00 }, + { 0xc001, 0x43 }, + { 0xc003, 0x22 }, + { 0xc004, 0x44 }, + { 0xc005, 0x44 }, + { 0xc006, 0x33 }, + { 0xc007, 0x64 }, + { 0xc008, 0x05 }, + { 0xc00a, 0xfc }, + { 0xc00b, 0x0f }, + { 0xc00c, 0x0e }, + { 0xc00d, 0xef }, + { 0xc00e, 0xe5 }, + { 0xc00f, 0xff }, + { 0xc120, 0xc0 }, + { 0xc121, 0x00 }, + { 0xc122, 0x00 }, + { 0xc123, 0x14 }, + { 0xc125, 0x00 }, + { 0xc130, 0x59 }, + { 0xc200, 0x00 }, + { 0xc201, 0x00 }, + { 0xc202, 0x00 }, + { 0xc203, 0x04 }, + { 0xc204, 0x00 }, + { 0xc205, 0x00 }, + { 0xc206, 0x68 }, + { 0xc207, 0x70 }, + { 0xc208, 0x00 }, + { 0xc20a, 0x00 }, + { 0xc20b, 0x01 }, + { 0xc20c, 0x7f }, + { 0xc20d, 0x01 }, + { 0xc20e, 0x7f }, + { 0xc300, 0x00 }, + { 0xc301, 0x00 }, + { 0xc303, 0x80 }, + { 0xc320, 0x00 }, + { 0xc321, 0x09 }, + { 0xc322, 0x02 }, + { 0xc400, 0x00 }, + { 0xc401, 0x00 }, + { 0xc402, 0x00 }, + { 0xc403, 0x00 }, + { 0xc404, 0x00 }, + { 0xc405, 0x00 }, + { 0xc406, 0x00 }, + { 0xc407, 0x00 }, + { 0xc408, 0x00 }, + { 0xc410, 0x04 }, + { 0xc430, 0x00 }, + { 0xc431, 0x00 }, + { 0xca00, 0x10 }, + { 0xca01, 0x00 }, + { 0xca02, 0x0b }, + { 0xca10, 0x10 }, + { 0xca11, 0x00 }, + { 0xca12, 0x0b }, + { 0xce04, 0x08 }, + { 0xce05, 0x00 }, + { 0xce06, 0x00 }, + { 0xce07, 0x00 }, + { 0xce60, 0x63 }, + { 0xcf04, 0x08 }, + { 0xcf05, 0x00 }, + { 0xcf06, 0x00 }, + { 0xcf07, 0x00 }, + { 0xdb00, 0x00 }, + { 0xdb08, 0x40 }, + { 0xdb12, 0x00 }, + { 0xdb35, 0x00 }, + { 0xdbb5, 0x00 }, + { 0xdbb6, 0x40 }, + { 0xdbb7, 0x00 }, + { 0xdbb8, 0x00 }, + { 0xdbc5, 0x00 }, + { 0xdbc6, 0x00 }, + { 0xdbc7, 0x00 }, + { 0xdbc8, 0x00 }, + { 0xdd08, 0x40 }, + { 0xdd12, 0x00 }, + { 0xdd35, 0x00 }, + { 0xddb5, 0x00 }, + { 0xddb6, 0x40 }, + { 0xddb7, 0x00 }, + { 0xddb8, 0x00 }, + { 0xddc5, 0x00 }, + { 0xddc6, 0x00 }, + { 0xddc7, 0x00 }, + { 0xddc8, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, + { 0xdf00, 0x00 }, + { 0xdf5f, 0x00 }, + { 0xdf60, 0x00 }, + { 0xe000, 0x08 }, + { 0xe300, 0xa0 }, + { 0xe400, 0x22 }, + { 0xe706, 0x2f }, + { 0xe707, 0x2f }, + { 0xe806, 0x2f }, + { 0xe807, 0x2f }, + { 0xea00, 0x43 }, + { 0xed00, 0x80 }, + { 0xed01, 0x0f }, + { 0xed02, 0xff }, + { 0xed03, 0x00 }, + { 0xed04, 0x00 }, + { 0xed05, 0x0f }, + { 0xed06, 0xff }, + { 0xf010, 0x10 }, + { 0xf011, 0xec }, + { 0xf012, 0x68 }, + { 0xf013, 0x21 }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0x00 }, + { 0xf105, 0x00 }, + { 0xf106, 0x00 }, + { 0xf107, 0x00 }, + { 0xf108, 0x00 }, + { 0xf109, 0x00 }, + { 0xf10a, 0x03 }, + { 0xf10b, 0x40 }, + { 0xf20b, 0x28 }, + { 0xf20d, 0x00 }, + { 0xf212, 0x00 }, + { 0xf21a, 0x00 }, + { 0xf223, 0x40 }, + { 0xf224, 0x00 }, + { 0xf225, 0x00 }, + { 0xf226, 0x00 }, + { 0xf227, 0x00 }, + { 0xf242, 0x0c }, + { 0xf800, 0x00 }, + { 0xf801, 0x12 }, + { 0xf802, 0xe0 }, + { 0xf803, 0x2f }, + { 0xf804, 0x00 }, + { 0xf805, 0x00 }, + { 0xf806, 0x07 }, + { 0xf807, 0xff }, +}; + +static bool rt1318_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000: + case 0xc301: + case 0xc410: + case 0xc430 ... 0xc431: + case 0xdb06: + case 0xdb12: + case 0xdb1d ... 0xdb1f: + case 0xdb35: + case 0xdb37: + case 0xdb8a ... 0xdb92: + case 0xdbc5 ... 0xdbc8: + case 0xdc2b ... 0xdc49: + case 0xdd0b: + case 0xdd12: + case 0xdd1d ... 0xdd1f: + case 0xdd35: + case 0xdd8a ... 0xdd92: + case 0xddc5 ... 0xddc8: + case 0xde2b ... 0xde44: + case 0xdf4a ... 0xdf55: + case 0xe224 ... 0xe23b: + case 0xea01: + case 0xebc5: + case 0xebc8: + case 0xebcb ... 0xebcc: + case 0xed03 ... 0xed06: + case 0xf010 ... 0xf014: + return true; + + default: + return false; + } +} + +static bool rt1318_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000 ... 0xc00f: + case 0xc120 ... 0xc130: + case 0xc200 ... 0xc20e: + case 0xc300 ... 0xc303: + case 0xc320 ... 0xc322: + case 0xc400 ... 0xc408: + case 0xc430 ... 0xc431: + case 0xca00 ... 0xca02: + case 0xca10 ... 0xca12: + case 0xcb00 ... 0xcb0b: + case 0xcc00 ... 0xcce5: + case 0xcd00 ... 0xcde5: + case 0xce00 ... 0xce6a: + case 0xcf00 ... 0xcf53: + case 0xd000 ... 0xd0cc: + case 0xd100 ... 0xd1b9: + case 0xdb00 ... 0xdc53: + case 0xdd00 ... 0xde53: + case 0xdf00 ... 0xdf6b: + case 0xe000: + case 0xe300: + case 0xe400: + case 0xe706 ... 0xe707: + case 0xe806 ... 0xe807: + case 0xea00: + case 0xeb00 ... 0xebcc: + case 0xec00 ... 0xecb9: + case 0xed00 ... 0xed06: + case 0xf010 ... 0xf014: + case 0xf102 ... 0xf10b: + case 0xf20b: + case 0xf20d ... 0xf242: + case 0xf800 ... 0xf807: + return true; + default: + return false; + } +} + +static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH); + break; + + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW); + break; + + default: + break; + } + return 0; +} + +static int rt1318_dvol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->rt1318_dvol = ucontrol->value.integer.value[0]; + + if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) { + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7, + rt1318->rt1318_dvol & 0xff); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7, + rt1318->rt1318_dvol & 0xff); + return 1; + } + + return 0; +} + +static int rt1318_dvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1318->rt1318_dvol; + + return 0; +} + +static const struct snd_kcontrol_new rt1318_snd_controls[] = { + SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0, + rt1318_dvol_get, rt1318_dvol_put), +}; + +static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + /* DACs */ + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + rt1318_dac_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("Amp"), +}; + +static const struct snd_soc_dapm_route rt1318_dapm_routes[] = { + {"DAC", NULL, "AIF1RX"}, + {"Amp", NULL, "DAC"}, +}; + +static int rt1318_get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 4, 8, 16, 24}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (lrclk) { + case RT1318_LRCLK_48000: + case RT1318_LRCLK_44100: + case RT1318_LRCLK_16000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4); + break; + case RT1318_LRCLK_96000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2); + break; + case RT1318_LRCLK_192000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1); + break; + default: + dev_err(component->dev, "Unsupported clock rate.\n"); + return -EINVAL; + } + + return 0; +} + +static int rt1318_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int data_len = 0, ch_len = 0; + int pre_div, ret; + + rt1318->lrck = params_rate(params); + pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + ret = rt1318_clk_ip_info(component, rt1318->lrck); + if (ret < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + data_len = RT1318_I2S_DL_20; + ch_len = RT1318_I2S_DL_20; + break; + case 24: + data_len = RT1318_I2S_DL_24; + ch_len = RT1318_I2S_DL_24; + break; + case 32: + data_len = RT1318_I2S_DL_32; + ch_len = RT1318_I2S_DL_32; + break; + case 8: + data_len = RT1318_I2S_DL_8; + ch_len = RT1318_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_CLK2, + RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK, + pre_div << RT1318_DIV_AP_SFT | + pre_div << RT1318_DIV_DAMOD_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK3, + RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK, + pre_div << RT1318_AD_STO1_SFT | + pre_div << RT1318_AD_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK4, + RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK, + pre_div << RT1318_AD_ANA_STO1_SFT | + pre_div << RT1318_AD_ANA_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK5, + RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK, + pre_div << RT1318_DIV_FIFO_IN_SFT | + pre_div << RT1318_DIV_FIFO_OUT_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK6, + RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK | + RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT | + pre_div << RT1318_DIV_AD_MONO_SFT | + pre_div << RT1318_DIV_POST_G_SFT); + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, + ch_len << RT1318_I2S_TX_CHL_SFT | + ch_len << RT1318_I2S_RX_CHL_SFT); + + return 0; +} + +static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val2 |= RT1318_TDM_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1318_FMT_LEFT_J; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1318_FMT_PCM_A_R; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1318_FMT_PCM_B_R; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_I2S_FMT_MASK, reg_val); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_TDM_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int reg_val = 0; + + if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src) + return 0; + + switch (clk_id) { + case RT1318_SCLK_S_BCLK: + reg_val |= RT1318_SYSCLK_BCLK; + break; + case RT1318_SCLK_S_SDW: + reg_val |= RT1318_SYSCLK_SDW; + break; + case RT1318_SCLK_S_PLL2F: + reg_val |= RT1318_SYSCLK_PLL2F; + break; + case RT1318_SCLK_S_PLL2B: + reg_val |= RT1318_SYSCLK_PLL2B; + break; + case RT1318_SCLK_S_MCLK: + reg_val |= RT1318_SYSCLK_MCLK; + break; + case RT1318_SCLK_S_RC0: + reg_val |= RT1318_SYSCLK_RC1; + break; + case RT1318_SCLK_S_RC1: + reg_val |= RT1318_SYSCLK_RC2; + break; + case RT1318_SCLK_S_RC2: + reg_val |= RT1318_SYSCLK_RC3; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1318->sysclk = freq; + rt1318->sysclk_src = clk_id; + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_SYSCLK_SEL_MASK, reg_val); + + return 0; +} + +static const struct pll_calc_map pll_preset_table[] = { + {512000, 4096000, 22, 190, 0, true, false}, + {1024000, 4096000, 22, 94, 0, true, false}, + {1024000, 16384000, 4, 190, 0, true, false}, + {1411200, 11289600, 6, 62, 0, true, false}, + {1536000, 12288000, 6, 62, 0, true, false}, + {2822400, 11289600, 6, 62, 0, true, false}, + {2822400, 45158400, 0, 62, 0, true, false}, + {2822400, 49152000, 0, 62, 0, true, false}, + {3072000, 12288000, 6, 62, 0, true, false}, + {3072000, 24576000, 2, 62, 0, true, false}, + {3072000, 49152000, 0, 62, 0, true, false}, + {6144000, 24576000, 2, 94, 4, false, false}, + {6144000, 49152000, 0, 30, 0, true, false}, + {6144000, 98304000, 0, 94, 4, false, true}, + {12288000, 49152000, 0, 62, 6, false, false}, +}; + +static int rt1318_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt1318_pll_code *pll_code) +{ + int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX; + int i, k, red, n_t, pll_out, in_t, out_t; + int n = 0, m = 0, m_t = 0; + int red_t = abs(freq_out - freq_in); + bool m_bypass = false, k_bypass = false; + + if (RT1318_PLL_INP_MAX < freq_in || RT1318_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { + if (freq_in == pll_preset_table[i].pll_in && + freq_out == pll_preset_table[i].pll_out) { + k = pll_preset_table[i].k; + m = pll_preset_table[i].m; + n = pll_preset_table[i].n; + m_bypass = pll_preset_table[i].m_bp; + k_bypass = pll_preset_table[i].k_bp; + goto code_find; + } + } + + k = 100000000 / freq_out - 2; + if (k > RT1318_PLL_K_MAX) + k = RT1318_PLL_K_MAX; + if (k < 0) { + k = 0; + k_bypass = true; + } + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = freq_in / (k_bypass ? 1 : (k + 2)); + pll_out = freq_out / (n_t + 2); + if (in_t < 0) + continue; + if (in_t == pll_out) { + m_bypass = true; + n = n_t; + goto code_find; + } + red = abs(in_t - pll_out); + if (red < red_t) { + m_bypass = true; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - pll_out); + if (red < red_t) { + m_bypass = false; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + + pll_code->m_bp = m_bypass; + pll_code->k_bp = k_bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = k; + return 0; +} + +static int rt1318_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + struct rt1318_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + rt1318->pll_in = 0; + rt1318->pll_out = 0; + return 0; + } + + if (source == rt1318->pll_src && freq_in == rt1318->pll_in && + freq_out == rt1318->pll_out) + return 0; + + switch (source) { + case RT1318_PLL_S_BCLK0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0); + break; + case RT1318_PLL_S_BCLK1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1); + break; + case RT1318_PLL_S_RC: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_RC); + break; + case RT1318_PLL_S_MCLK: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK); + break; + case RT1318_PLL_S_SDW_IN_PLL: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1); + break; + case RT1318_PLL_S_SDW_0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2); + break; + case RT1318_PLL_S_SDW_1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3); + break; + case RT1318_PLL_S_SDW_2: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt1318_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_update_bits(rt1318->regmap, RT1318_PLL1_K, + RT1318_K_PLL1_MASK, pll_code.k_code); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_M, + RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code)); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8, + RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0, + RT1318_N_7_0_PLL1_MASK, pll_code.n_code); + + rt1318->pll_in = freq_in; + rt1318->pll_out = freq_out; + rt1318->pll_src = source; + + return 0; +} + +static int rt1318_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int cn = 0, cl = 0, rx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 4: + cn |= RT1318_I2S_CH_TX_4CH; + cn |= RT1318_I2S_CH_RX_4CH; + break; + case 6: + cn |= RT1318_I2S_CH_TX_6CH; + cn |= RT1318_I2S_CH_RX_6CH; + break; + case 8: + cn |= RT1318_I2S_CH_TX_8CH; + cn |= RT1318_I2S_CH_RX_8CH; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + cl |= RT1318_I2S_TX_CHL_20; + cl |= RT1318_I2S_RX_CHL_20; + break; + case 24: + cl |= RT1318_I2S_TX_CHL_24; + cl |= RT1318_I2S_RX_CHL_24; + break; + case 32: + cl |= RT1318_I2S_TX_CHL_32; + cl |= RT1318_I2S_RX_CHL_32; + break; + case 8: + cl |= RT1318_I2S_TX_CHL_8; + cl |= RT1318_I2S_RX_CHL_8; + break; + case 16: + break; + default: + return -EINVAL; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl); + +_set_tdm_err_: + return ret; +} + +static int rt1318_probe(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->component = component; + + schedule_work(&rt1318->cali_work); + rt1318->rt1318_dvol = RT1318_DVOL_STEP; + + return 0; +} + +static void rt1318_remove(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + cancel_work_sync(&rt1318->cali_work); +} + +#ifdef CONFIG_PM +static int rt1318_suspend(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, true); + regcache_mark_dirty(rt1318->regmap); + return 0; +} + +static int rt1318_resume(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, false); + regcache_sync(rt1318->regmap); + return 0; +} +#else +#define rt1318_suspend NULL +#define rt1318_resume NULL +#endif + +#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1318_aif_dai_ops = { + .hw_params = rt1318_hw_params, + .set_fmt = rt1318_set_dai_fmt, + .set_sysclk = rt1318_set_dai_sysclk, + .set_pll = rt1318_set_dai_pll, + .set_tdm_slot = rt1318_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1318_dai[] = { + { + .name = "rt1318-aif", + .id = 0, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1318_STEREO_RATES, + .formats = RT1318_FORMATS, + }, + .ops = &rt1318_aif_dai_ops, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1318 = { + .probe = rt1318_probe, + .remove = rt1318_remove, + .suspend = rt1318_suspend, + .resume = rt1318_resume, + .controls = rt1318_snd_controls, + .num_controls = ARRAY_SIZE(rt1318_snd_controls), + .dapm_widgets = rt1318_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets), + .dapm_routes = rt1318_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1318_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1318_readable_register, + .volatile_reg = rt1318_volatile_register, + .max_register = 0x41001888, + .reg_defaults = rt1318_reg, + .num_reg_defaults = ARRAY_SIZE(rt1318_reg), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt1318_i2c_id[] = { + { "rt1318" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); + +static const struct of_device_id rt1318_of_match[] = { + { .compatible = "realtek,rt1318", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1318_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1318_acpi_match[] = { + { "10EC1318", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match); +#endif + +static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev) +{ + device_property_read_u32(dev, "realtek,r0_l", + &rt1318->pdata.init_r0_l); + device_property_read_u32(dev, "realtek,r0_r", + &rt1318->pdata.init_r0_r); + + return 0; +} + +static void rt1318_calibration_sequence(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_CLK1, 0x22); + regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40); + regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20); + regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00); + regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B); + regmap_write(rt1318->regmap, RT1318_TCON, 0x1C); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2); +} + +static void rt1318_r0_calculate(struct rt1318_priv *rt1318) +{ + unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3; + unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3; + unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor; + unsigned int format = 16777216; /* 2^24 */ + + regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0); + regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1); + regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2); + regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3); + r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3; + r0_l_integer = format / r0_l; + r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10; + + regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0); + regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1); + regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2); + regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3); + r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3; + r0_r_integer = format / r0_r; + r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10; + + dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor); + dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor); +} + +static void rt1318_r0_restore(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24, + (rt1318->pdata.init_r0_l >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16, + (rt1318->pdata.init_r0_l >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8, + (rt1318->pdata.init_r0_l >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0, + (rt1318->pdata.init_r0_l >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24, + (rt1318->pdata.init_r0_r >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16, + (rt1318->pdata.init_r0_r >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8, + (rt1318->pdata.init_r0_r >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0, + (rt1318->pdata.init_r0_r >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80); + regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc); + regmap_write(rt1318->regmap, RT1318_TCON, 0x9c); +} + +static int rt1318_calibrate(struct rt1318_priv *rt1318) +{ + int chk_cnt = 30, count = 0; + int val, val2; + + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1); + usleep_range(0, 10000); + rt1318_calibration_sequence(rt1318); + + while (count < chk_cnt) { + msleep(100); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + val = (val >> 1) & 0x1; + val2 = (val2 >> 1) & 0x1; + if (val & val2) { + dev_dbg(rt1318->component->dev, "Calibration done.\n"); + break; + } + count++; + if (count == chk_cnt) { + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + return RT1318_R0_CALIB_NOT_DONE; + } + } + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + if ((val & 0x1) & (val2 & 0x1)) + return RT1318_R0_IN_RANGE; + else + return RT1318_R0_OUT_OF_RANGE; +} + +static void rt1318_calibration_work(struct work_struct *work) +{ + struct rt1318_priv *rt1318 = + container_of(work, struct rt1318_priv, cali_work); + int ret; + + if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r) + rt1318_r0_restore(rt1318); + else { + ret = rt1318_calibrate(rt1318); + if (ret == RT1318_R0_IN_RANGE) + rt1318_r0_calculate(rt1318); + dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret); + } +} + +static int rt1318_i2c_probe(struct i2c_client *i2c) +{ + struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt1318_priv *rt1318; + int ret, val, val2, dev_id; + + rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv), + GFP_KERNEL); + if (!rt1318) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1318); + + if (pdata) + rt1318->pdata = *pdata; + else + rt1318_parse_dt(rt1318, &i2c->dev); + + rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap); + if (IS_ERR(rt1318->regmap)) { + ret = PTR_ERR(rt1318->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val); + regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2); + dev_id = (val << 8) | val2; + if (dev_id != 0x6821) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt1318\n", + dev_id); + return -ENODEV; + } + + ret = regmap_register_patch(rt1318->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + INIT_WORK(&rt1318->cali_work, rt1318_calibration_work); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai)); +} + +static struct i2c_driver rt1318_i2c_driver = { + .driver = { + .name = "rt1318", + .of_match_table = of_match_ptr(rt1318_of_match), + .acpi_match_table = ACPI_PTR(rt1318_acpi_match), + }, + .probe = rt1318_i2c_probe, + .id_table = rt1318_i2c_id, +}; +module_i2c_driver(rt1318_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1318 driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h new file mode 100644 index 000000000000..cec40b484216 --- /dev/null +++ b/sound/soc/codecs/rt1318.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ +#include <sound/rt1318.h> + +#ifndef __RT1318_H__ +#define __RT1318_H__ + +struct rt1318_priv { + struct snd_soc_component *component; + struct rt1318_platform_data pdata; + struct work_struct cali_work; + struct regmap *regmap; + + unsigned int r0_l_integer; + unsigned int r0_l_factor; + unsigned int r0_r_integer; + unsigned int r0_r_factor; + int rt1318_init; + int rt1318_dvol; + int sysclk_src; + int sysclk; + int lrck; + int bclk; + int master; + int pll_src; + int pll_in; + int pll_out; +}; + +#define RT1318_PLL_INP_MAX 40000000 +#define RT1318_PLL_INP_MIN 256000 +#define RT1318_PLL_N_MAX 0x1ff +#define RT1318_PLL_K_MAX 0x1f +#define RT1318_PLL_M_MAX 0x1f + +#define RT1318_LRCLK_192000 192000 +#define RT1318_LRCLK_96000 96000 +#define RT1318_LRCLK_48000 48000 +#define RT1318_LRCLK_44100 44100 +#define RT1318_LRCLK_16000 16000 +#define RT1318_DVOL_STEP 383 + +#define RT1318_CLK1 0xc001 +#define RT1318_CLK2 0xc003 +#define RT1318_CLK3 0xc004 +#define RT1318_CLK4 0xc005 +#define RT1318_CLK5 0xc006 +#define RT1318_CLK6 0xc007 +#define RT1318_CLK7 0xc008 +#define RT1318_PWR_STA1 0xc121 +#define RT1318_SPK_VOL_TH 0xc130 +#define RT1318_TCON 0xc203 +#define RT1318_SRC_TCON 0xc204 +#define RT1318_TCON_RELATE 0xc206 +#define RT1318_DA_VOL_L_8 0xc20b +#define RT1318_DA_VOL_L_1_7 0xc20c +#define RT1318_DA_VOL_R_8 0xc20d +#define RT1318_DA_VOL_R_1_7 0xc20e +#define RT1318_FEEDBACK_PATH 0xc321 +#define RT1318_STP_TEMP_L 0xdb00 +#define RT1318_STP_SEL_L 0xdb08 +#define RT1318_STP_R0_EN_L 0xdb12 +#define RT1318_R0_CMP_L_FLAG 0xdb35 +#define RT1318_PRE_R0_L_24 0xdbb5 +#define RT1318_PRE_R0_L_23_16 0xdbb6 +#define RT1318_PRE_R0_L_15_8 0xdbb7 +#define RT1318_PRE_R0_L_7_0 0xdbb8 +#define RT1318_R0_L_24 0xdbc5 +#define RT1318_R0_L_23_16 0xdbc6 +#define RT1318_R0_L_15_8 0xdbc7 +#define RT1318_R0_L_7_0 0xdbc8 +#define RT1318_STP_SEL_R 0xdd08 +#define RT1318_STP_R0_EN_R 0xdd12 +#define RT1318_R0_CMP_R_FLAG 0xdd35 +#define RT1318_PRE_R0_R_24 0xddb5 +#define RT1318_PRE_R0_R_23_16 0xddb6 +#define RT1318_PRE_R0_R_15_8 0xddb7 +#define RT1318_PRE_R0_R_7_0 0xddb8 +#define RT1318_R0_R_24 0xddc5 +#define RT1318_R0_R_23_16 0xddc6 +#define RT1318_R0_R_15_8 0xddc7 +#define RT1318_R0_R_7_0 0xddc8 +#define RT1318_DEV_ID1 0xf012 +#define RT1318_DEV_ID2 0xf013 +#define RT1318_PLL1_K 0xf20d +#define RT1318_PLL1_M 0xf20f +#define RT1318_PLL1_N_8 0xf211 +#define RT1318_PLL1_N_7_0 0xf212 +#define RT1318_SINE_GEN0 0xf800 +#define RT1318_TDM_CTRL1 0xf900 +#define RT1318_TDM_CTRL2 0xf901 +#define RT1318_TDM_CTRL3 0xf902 +#define RT1318_TDM_CTRL9 0xf908 + + +/* Clock-1 (0xC001) */ +#define RT1318_PLLIN_MASK (0x7 << 4) +#define RT1318_PLLIN_BCLK0 (0x0 << 4) +#define RT1318_PLLIN_BCLK1 (0x1 << 4) +#define RT1318_PLLIN_RC (0x2 << 4) +#define RT1318_PLLIN_MCLK (0x3 << 4) +#define RT1318_PLLIN_SDW1 (0x4 << 4) +#define RT1318_PLLIN_SDW2 (0x5 << 4) +#define RT1318_PLLIN_SDW3 (0x6 << 4) +#define RT1318_PLLIN_SDW4 (0x7 << 4) +#define RT1318_SYSCLK_SEL_MASK (0x7 << 0) +#define RT1318_SYSCLK_BCLK (0x0 << 0) +#define RT1318_SYSCLK_SDW (0x1 << 0) +#define RT1318_SYSCLK_PLL2F (0x2 << 0) +#define RT1318_SYSCLK_PLL2B (0x3 << 0) +#define RT1318_SYSCLK_MCLK (0x4 << 0) +#define RT1318_SYSCLK_RC1 (0x5 << 0) +#define RT1318_SYSCLK_RC2 (0x6 << 0) +#define RT1318_SYSCLK_RC3 (0x7 << 0) +/* Clock-2 (0xC003) */ +#define RT1318_DIV_AP_MASK (0x3 << 4) +#define RT1318_DIV_AP_SFT 4 +#define RT1318_DIV_AP_DIV1 (0x0 << 4) +#define RT1318_DIV_AP_DIV2 (0x1 << 4) +#define RT1318_DIV_AP_DIV4 (0x2 << 4) +#define RT1318_DIV_AP_DIV8 (0x3 << 4) +#define RT1318_DIV_DAMOD_MASK (0x3 << 0) +#define RT1318_DIV_DAMOD_SFT 0 +#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0) +#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0) +#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0) +#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0) +/* Clock-3 (0xC004) */ +#define RT1318_AD_STO1_MASK (0x7 << 4) +#define RT1318_AD_STO1_SFT 4 +#define RT1318_AD_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_STO2_MASK (0x7 << 0) +#define RT1318_AD_STO2_SFT 0 +#define RT1318_AD_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_STO2_SFT 0 +/* Clock-4 (0xC005) */ +#define RT1318_AD_ANA_STO1_MASK (0x7 << 4) +#define RT1318_AD_ANA_STO1_SFT 4 +#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_ANA_STO2_MASK (0x7 << 0) +#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_ANA_STO2_SFT 0 +/* Clock-5 (0xC006) */ +#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4) +#define RT1318_DIV_FIFO_IN_SFT 4 +#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4) +#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4) +#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4) +#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4) +#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0) +#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0) +#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0) +#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_SFT 0 +/* Clock-6 (0xC007) */ +#define RT1318_DIV_NLMS_MASK (0x3 << 6) +#define RT1318_DIV_NLMS_SFT 6 +#define RT1318_DIV_NLMS_DIV1 (0x0 << 6) +#define RT1318_DIV_NLMS_DIV2 (0x1 << 6) +#define RT1318_DIV_NLMS_DIV4 (0x2 << 6) +#define RT1318_DIV_NLMS_DIV8 (0x3 << 6) +#define RT1318_DIV_AD_MONO_MASK (0x7 << 3) +#define RT1318_DIV_AD_MONO_SFT 3 +#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3) +#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3) +#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3) +#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3) +#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3) +#define RT1318_DIV_POST_G_MASK (0x7 << 0) +#define RT1318_DIV_POST_G_SFT 0 +#define RT1318_DIV_POST_G_DIV1 (0x0 << 0) +#define RT1318_DIV_POST_G_DIV2 (0x1 << 0) +#define RT1318_DIV_POST_G_DIV4 (0x2 << 0) +#define RT1318_DIV_POST_G_DIV8 (0x3 << 0) +#define RT1318_DIV_POST_G_DIV16 (0x4 << 0) +/* Power Status 1 (0xC121) */ +#define RT1318_PDB_CTRL_MASK (0x1) +#define RT1318_PDB_CTRL_LOW (0x0) +#define RT1318_PDB_CTRL_HIGH (0x1) +#define RT1318_PDB_CTRL_SFT 0 +/* SRC Tcon(0xc204) */ +#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6) +#define RT1318_SRCIN_IN_48K (0x0 << 6) +#define RT1318_SRCIN_IN_44P1 (0x1 << 6) +#define RT1318_SRCIN_IN_32K (0x2 << 6) +#define RT1318_SRCIN_IN_16K (0x3 << 6) +#define RT1318_SRCIN_F12288_MASK (0x3 << 4) +#define RT1318_SRCIN_TCON1 (0x0 << 4) +#define RT1318_SRCIN_TCON2 (0x1 << 4) +#define RT1318_SRCIN_TCON4 (0x2 << 4) +#define RT1318_SRCIN_TCON8 (0x3 << 4) +#define RT1318_SRCIN_DACLK_MASK (0x3 << 2) +#define RT1318_DACLK_TCON1 (0x0 << 2) +#define RT1318_DACLK_TCON2 (0x1 << 2) +#define RT1318_DACLK_TCON4 (0x2 << 2) +#define RT1318_DACLK_TCON8 (0x3 << 2) +/* R0 Compare Flag (0xDB35) */ +#define RT1318_R0_RANGE_MASK (0x1) +#define RT1318_R0_OUTOFRANGE (0x0) +#define RT1318_R0_INRANGE (0x1) +/* PLL internal setting (0xF20D), K value */ +#define RT1318_K_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF20F), M value */ +#define RT1318_M_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF211), N_8 value */ +#define RT1318_N_8_PLL1_MASK (0x1 << 0) +/* PLL internal setting (0xF212), N_7_0 value */ +#define RT1318_N_7_0_PLL1_MASK (0xff << 0) +/* TDM CTRL 1 (0xf900) */ +#define RT1318_TDM_BCLK_MASK (0x1 << 7) +#define RT1318_TDM_BCLK_NORM (0x0 << 7) +#define RT1318_TDM_BCLK_INV (0x1 << 7) +#define RT1318_I2S_FMT_MASK (0x7 << 0) +#define RT1318_FMT_I2S (0x0 << 0) +#define RT1318_FMT_LEFT_J (0x1 << 0) +#define RT1318_FMT_PCM_A_R (0x2 << 0) +#define RT1318_FMT_PCM_B_R (0x3 << 0) +#define RT1318_FMT_PCM_A_F (0x6 << 0) +#define RT1318_FMT_PCM_B_F (0x7 << 0) +#define RT1318_I2S_FMT_SFT 0 +/* TDM CTRL 2 (0xf901) */ +#define RT1318_I2S_CH_TX_MASK (0x3 << 6) +#define RT1318_I2S_CH_TX_2CH (0x0 << 6) +#define RT1318_I2S_CH_TX_4CH (0x1 << 6) +#define RT1318_I2S_CH_TX_6CH (0x2 << 6) +#define RT1318_I2S_CH_TX_8CH (0x3 << 6) +#define RT1318_I2S_CH_RX_MASK (0x3 << 4) +#define RT1318_I2S_CH_RX_2CH (0x0 << 4) +#define RT1318_I2S_CH_RX_4CH (0x1 << 4) +#define RT1318_I2S_CH_RX_6CH (0x2 << 4) +#define RT1318_I2S_CH_RX_8CH (0x3 << 4) +#define RT1318_I2S_DL_MASK 0x7 +#define RT1318_I2S_DL_SFT 0 +#define RT1318_I2S_DL_16 0x0 +#define RT1318_I2S_DL_20 0x1 +#define RT1318_I2S_DL_24 0x2 +#define RT1318_I2S_DL_32 0x3 +#define RT1318_I2S_DL_8 0x4 +/* TDM CTRL 3 (0xf902) */ +#define RT1318_I2S_TX_CHL_MASK (0x7 << 4) +#define RT1318_I2S_TX_CHL_SFT 4 +#define RT1318_I2S_TX_CHL_16 (0x0 << 4) +#define RT1318_I2S_TX_CHL_20 (0x1 << 4) +#define RT1318_I2S_TX_CHL_24 (0x2 << 4) +#define RT1318_I2S_TX_CHL_32 (0x3 << 4) +#define RT1318_I2S_TX_CHL_8 (0x4 << 4) +#define RT1318_I2S_RX_CHL_MASK (0x7 << 0) +#define RT1318_I2S_RX_CHL_SFT 0 +#define RT1318_I2S_RX_CHL_16 (0x0 << 0) +#define RT1318_I2S_RX_CHL_20 (0x1 << 0) +#define RT1318_I2S_RX_CHL_24 (0x2 << 0) +#define RT1318_I2S_RX_CHL_32 (0x3 << 0) +#define RT1318_I2S_RX_CHL_8 (0x4 << 0) +/* TDM CTRL 9 (0xf908) */ +#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4) +#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7 +#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4 +#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0 + +#define RT1318_REG_DISP_LEN 23 + +/* System Clock Source */ +enum { + RT1318_SCLK_S_BCLK, + RT1318_SCLK_S_SDW, + RT1318_SCLK_S_PLL2F, + RT1318_SCLK_S_PLL2B, + RT1318_SCLK_S_MCLK, + RT1318_SCLK_S_RC0, + RT1318_SCLK_S_RC1, + RT1318_SCLK_S_RC2, +}; + +/* PLL Source */ +enum { + RT1318_PLL_S_BCLK0, + RT1318_PLL_S_BCLK1, + RT1318_PLL_S_RC, + RT1318_PLL_S_MCLK, + RT1318_PLL_S_SDW_IN_PLL, + RT1318_PLL_S_SDW_0, + RT1318_PLL_S_SDW_1, + RT1318_PLL_S_SDW_2, +}; + +/* TDM channel */ +enum { + RT1318_2CH, + RT1318_4CH, + RT1318_6CH, + RT1318_8CH, +}; + +/* R0 calibration result */ +enum { + RT1318_R0_OUT_OF_RANGE, + RT1318_R0_IN_RANGE, + RT1318_R0_CALIB_NOT_DONE, +}; + +/* PLL pre-defined M/N/K */ + +struct pll_calc_map { + unsigned int pll_in; + unsigned int pll_out; + int k; + int n; + int m; + bool m_bp; + bool k_bp; +}; + +struct rt1318_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + bool k_bp; /* Indicates bypass k code or not. */ + int m_code; + int n_code; + int k_code; +}; + +#endif /* __RT1318_H__ */ diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c new file mode 100644 index 000000000000..2916fa77b791 --- /dev/null +++ b/sound/soc/codecs/rt1320-sdw.c @@ -0,0 +1,2260 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1320-sdw.c -- rt1320 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/dmi.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/sdw.h> +#include "rt1320-sdw.h" + +/* + * The 'blind writes' is an SDCA term to deal with platform-specific initialization. + * It might include vendor-specific or SDCA control registers. + */ +static const struct reg_sequence rt1320_blind_write[] = { + { 0xc003, 0xe0 }, + { 0xc01b, 0xfc }, + { 0xc5c3, 0xf2 }, + { 0xc5c2, 0x00 }, + { 0xc5c6, 0x10 }, + { 0xc5c4, 0x12 }, + { 0xc5c8, 0x03 }, + { 0xc5d8, 0x0a }, + { 0xc5f7, 0x22 }, + { 0xc5f6, 0x22 }, + { 0xc5d0, 0x0f }, + { 0xc5d1, 0x89 }, + { 0xc057, 0x51 }, + { 0xc054, 0x35 }, + { 0xc053, 0x55 }, + { 0xc052, 0x55 }, + { 0xc051, 0x13 }, + { 0xc050, 0x15 }, + { 0xc060, 0x77 }, + { 0xc061, 0x55 }, + { 0xc063, 0x55 }, + { 0xc065, 0xa5 }, + { 0xc06b, 0x0a }, + { 0xca05, 0xd6 }, + { 0xca25, 0xd6 }, + { 0xcd00, 0x05 }, + { 0xc604, 0x40 }, + { 0xc609, 0x40 }, + { 0xc046, 0xff }, + { 0xc045, 0xff }, + { 0xc044, 0xff }, + { 0xc043, 0xff }, + { 0xc042, 0xff }, + { 0xc041, 0xff }, + { 0xc040, 0xff }, + { 0xcc10, 0x01 }, + { 0xc700, 0xf0 }, + { 0xc701, 0x13 }, + { 0xc901, 0x04 }, + { 0xc900, 0x73 }, + { 0xde03, 0x05 }, + { 0xdd0b, 0x0d }, + { 0xdd0a, 0xff }, + { 0xdd09, 0x0d }, + { 0xdd08, 0xff }, + { 0xc570, 0x08 }, + { 0xe803, 0xbe }, + { 0xc003, 0xc0 }, + { 0xc081, 0xfe }, + { 0xce31, 0x0d }, + { 0xce30, 0xae }, + { 0xce37, 0x0b }, + { 0xce36, 0xd2 }, + { 0xce39, 0x04 }, + { 0xce38, 0x80 }, + { 0xce3f, 0x00 }, + { 0xce3e, 0x00 }, + { 0xd470, 0x8b }, + { 0xd471, 0x18 }, + { 0xc019, 0x10 }, + { 0xd487, 0x3f }, + { 0xd486, 0xc3 }, +}; + +/* + * The 'patch code' is written to the patch code area. + * The patch code area is used for SDCA register expansion flexibility. + */ +static const struct reg_sequence rt1320_patch_code_write[] = { + { 0x10007000, 0x37 }, + { 0x10007001, 0x77 }, + { 0x10007002, 0x00 }, + { 0x10007003, 0x10 }, + { 0x10007004, 0xb7 }, + { 0x10007005, 0xe7 }, + { 0x10007006, 0x00 }, + { 0x10007007, 0x10 }, + { 0x10007008, 0x13 }, + { 0x10007009, 0x07 }, + { 0x1000700a, 0x07 }, + { 0x1000700b, 0x40 }, + { 0x1000700c, 0x23 }, + { 0x1000700d, 0xae }, + { 0x1000700e, 0xe7 }, + { 0x1000700f, 0xda }, + { 0x10007010, 0x37 }, + { 0x10007011, 0x77 }, + { 0x10007012, 0x00 }, + { 0x10007013, 0x10 }, + { 0x10007014, 0x13 }, + { 0x10007015, 0x07 }, + { 0x10007016, 0x47 }, + { 0x10007017, 0x61 }, + { 0x10007018, 0x23 }, + { 0x10007019, 0xa4 }, + { 0x1000701a, 0xe7 }, + { 0x1000701b, 0xde }, + { 0x1000701c, 0x37 }, + { 0x1000701d, 0x77 }, + { 0x1000701e, 0x00 }, + { 0x1000701f, 0x10 }, + { 0x10007020, 0x13 }, + { 0x10007021, 0x07 }, + { 0x10007022, 0x07 }, + { 0x10007023, 0x52 }, + { 0x10007024, 0x23 }, + { 0x10007025, 0xae }, + { 0x10007026, 0xe7 }, + { 0x10007027, 0xde }, + { 0x10007028, 0x37 }, + { 0x10007029, 0x77 }, + { 0x1000702a, 0x00 }, + { 0x1000702b, 0x10 }, + { 0x1000702c, 0x13 }, + { 0x1000702d, 0x07 }, + { 0x1000702e, 0x47 }, + { 0x1000702f, 0x54 }, + { 0x10007030, 0x23 }, + { 0x10007031, 0xaa }, + { 0x10007032, 0xe7 }, + { 0x10007033, 0xe4 }, + { 0x10007034, 0x37 }, + { 0x10007035, 0x87 }, + { 0x10007036, 0x00 }, + { 0x10007037, 0x10 }, + { 0x10007038, 0x13 }, + { 0x10007039, 0x07 }, + { 0x1000703a, 0x47 }, + { 0x1000703b, 0x81 }, + { 0x1000703c, 0x23 }, + { 0x1000703d, 0xa2 }, + { 0x1000703e, 0xe7 }, + { 0x1000703f, 0xe8 }, + { 0x10007040, 0x23 }, + { 0x10007041, 0xa4 }, + { 0x10007042, 0xe7 }, + { 0x10007043, 0xe8 }, + { 0x10007044, 0x37 }, + { 0x10007045, 0x77 }, + { 0x10007046, 0x00 }, + { 0x10007047, 0x10 }, + { 0x10007048, 0x13 }, + { 0x10007049, 0x07 }, + { 0x1000704a, 0x07 }, + { 0x1000704b, 0x59 }, + { 0x1000704c, 0x23 }, + { 0x1000704d, 0xa8 }, + { 0x1000704e, 0xe7 }, + { 0x1000704f, 0xea }, + { 0x10007050, 0x37 }, + { 0x10007051, 0x77 }, + { 0x10007052, 0x00 }, + { 0x10007053, 0x10 }, + { 0x10007054, 0x13 }, + { 0x10007055, 0x07 }, + { 0x10007056, 0x07 }, + { 0x10007057, 0x78 }, + { 0x10007058, 0x23 }, + { 0x10007059, 0xa6 }, + { 0x1000705a, 0xe7 }, + { 0x1000705b, 0xec }, + { 0x1000705c, 0x67 }, + { 0x1000705d, 0x80 }, + { 0x1000705e, 0x00 }, + { 0x1000705f, 0x00 }, + { 0x10007400, 0x37 }, + { 0x10007401, 0xd7 }, + { 0x10007402, 0x00 }, + { 0x10007403, 0x00 }, + { 0x10007404, 0x83 }, + { 0x10007405, 0x27 }, + { 0x10007406, 0x47 }, + { 0x10007407, 0x56 }, + { 0x10007408, 0xb7 }, + { 0x10007409, 0x06 }, + { 0x1000740a, 0x00 }, + { 0x1000740b, 0x02 }, + { 0x1000740c, 0xb3 }, + { 0x1000740d, 0xf7 }, + { 0x1000740e, 0xd7 }, + { 0x1000740f, 0x00 }, + { 0x10007410, 0x63 }, + { 0x10007411, 0x8a }, + { 0x10007412, 0x07 }, + { 0x10007413, 0x00 }, + { 0x10007414, 0x93 }, + { 0x10007415, 0x06 }, + { 0x10007416, 0x10 }, + { 0x10007417, 0x00 }, + { 0x10007418, 0x23 }, + { 0x10007419, 0x83 }, + { 0x1000741a, 0xd1 }, + { 0x1000741b, 0x44 }, + { 0x1000741c, 0x93 }, + { 0x1000741d, 0x07 }, + { 0x1000741e, 0xf0 }, + { 0x1000741f, 0xff }, + { 0x10007420, 0x23 }, + { 0x10007421, 0x22 }, + { 0x10007422, 0xf7 }, + { 0x10007423, 0x56 }, + { 0x10007424, 0x37 }, + { 0x10007425, 0xd7 }, + { 0x10007426, 0x00 }, + { 0x10007427, 0x00 }, + { 0x10007428, 0x83 }, + { 0x10007429, 0x27 }, + { 0x1000742a, 0x47 }, + { 0x1000742b, 0x58 }, + { 0x1000742c, 0x93 }, + { 0x1000742d, 0xf7 }, + { 0x1000742e, 0x17 }, + { 0x1000742f, 0x00 }, + { 0x10007430, 0x63 }, + { 0x10007431, 0x86 }, + { 0x10007432, 0x07 }, + { 0x10007433, 0x00 }, + { 0x10007434, 0x93 }, + { 0x10007435, 0x07 }, + { 0x10007436, 0x10 }, + { 0x10007437, 0x00 }, + { 0x10007438, 0x23 }, + { 0x10007439, 0x22 }, + { 0x1000743a, 0xf7 }, + { 0x1000743b, 0x58 }, + { 0x1000743c, 0xb7 }, + { 0x1000743d, 0xd7 }, + { 0x1000743e, 0x00 }, + { 0x1000743f, 0x00 }, + { 0x10007440, 0x03 }, + { 0x10007441, 0xa7 }, + { 0x10007442, 0x47 }, + { 0x10007443, 0x58 }, + { 0x10007444, 0xb7 }, + { 0x10007445, 0x07 }, + { 0x10007446, 0x00 }, + { 0x10007447, 0x04 }, + { 0x10007448, 0x33 }, + { 0x10007449, 0x77 }, + { 0x1000744a, 0xf7 }, + { 0x1000744b, 0x00 }, + { 0x1000744c, 0x93 }, + { 0x1000744d, 0x07 }, + { 0x1000744e, 0x00 }, + { 0x1000744f, 0x00 }, + { 0x10007450, 0x63 }, + { 0x10007451, 0x0e }, + { 0x10007452, 0x07 }, + { 0x10007453, 0x04 }, + { 0x10007454, 0x37 }, + { 0x10007455, 0x07 }, + { 0x10007456, 0x00 }, + { 0x10007457, 0x11 }, + { 0x10007458, 0x03 }, + { 0x10007459, 0x47 }, + { 0x1000745a, 0x87 }, + { 0x1000745b, 0x0e }, + { 0x1000745c, 0x93 }, + { 0x1000745d, 0x06 }, + { 0x1000745e, 0x40 }, + { 0x1000745f, 0x00 }, + { 0x10007460, 0x13 }, + { 0x10007461, 0x77 }, + { 0x10007462, 0xf7 }, + { 0x10007463, 0x0f }, + { 0x10007464, 0x63 }, + { 0x10007465, 0x02 }, + { 0x10007466, 0xd7 }, + { 0x10007467, 0x0a }, + { 0x10007468, 0x93 }, + { 0x10007469, 0x06 }, + { 0x1000746a, 0x70 }, + { 0x1000746b, 0x00 }, + { 0x1000746c, 0x63 }, + { 0x1000746d, 0x10 }, + { 0x1000746e, 0xd7 }, + { 0x1000746f, 0x04 }, + { 0x10007470, 0x93 }, + { 0x10007471, 0x07 }, + { 0x10007472, 0x60 }, + { 0x10007473, 0x06 }, + { 0x10007474, 0x37 }, + { 0x10007475, 0xd7 }, + { 0x10007476, 0x00 }, + { 0x10007477, 0x00 }, + { 0x10007478, 0x83 }, + { 0x10007479, 0x46 }, + { 0x1000747a, 0x77 }, + { 0x1000747b, 0xa6 }, + { 0x1000747c, 0x93 }, + { 0x1000747d, 0xe6 }, + { 0x1000747e, 0x06 }, + { 0x1000747f, 0xf8 }, + { 0x10007480, 0x93 }, + { 0x10007481, 0xf6 }, + { 0x10007482, 0xf6 }, + { 0x10007483, 0x0f }, + { 0x10007484, 0xa3 }, + { 0x10007485, 0x03 }, + { 0x10007486, 0xd7 }, + { 0x10007487, 0xa6 }, + { 0x10007488, 0x83 }, + { 0x10007489, 0x46 }, + { 0x1000748a, 0x77 }, + { 0x1000748b, 0xa8 }, + { 0x1000748c, 0x93 }, + { 0x1000748d, 0xe6 }, + { 0x1000748e, 0x06 }, + { 0x1000748f, 0xf8 }, + { 0x10007490, 0x93 }, + { 0x10007491, 0xf6 }, + { 0x10007492, 0xf6 }, + { 0x10007493, 0x0f }, + { 0x10007494, 0xa3 }, + { 0x10007495, 0x03 }, + { 0x10007496, 0xd7 }, + { 0x10007497, 0xa8 }, + { 0x10007498, 0xb7 }, + { 0x10007499, 0xc6 }, + { 0x1000749a, 0x00 }, + { 0x1000749b, 0x00 }, + { 0x1000749c, 0x23 }, + { 0x1000749d, 0x84 }, + { 0x1000749e, 0xf6 }, + { 0x1000749f, 0x06 }, + { 0x100074a0, 0xa3 }, + { 0x100074a1, 0x84 }, + { 0x100074a2, 0xf6 }, + { 0x100074a3, 0x06 }, + { 0x100074a4, 0xb7 }, + { 0x100074a5, 0x06 }, + { 0x100074a6, 0x00 }, + { 0x100074a7, 0x04 }, + { 0x100074a8, 0x23 }, + { 0x100074a9, 0x22 }, + { 0x100074aa, 0xd7 }, + { 0x100074ab, 0x58 }, + { 0x100074ac, 0x37 }, + { 0x100074ad, 0xd7 }, + { 0x100074ae, 0x00 }, + { 0x100074af, 0x00 }, + { 0x100074b0, 0x03 }, + { 0x100074b1, 0x27 }, + { 0x100074b2, 0x47 }, + { 0x100074b3, 0x58 }, + { 0x100074b4, 0xb7 }, + { 0x100074b5, 0x06 }, + { 0x100074b6, 0x00 }, + { 0x100074b7, 0x08 }, + { 0x100074b8, 0x33 }, + { 0x100074b9, 0x77 }, + { 0x100074ba, 0xd7 }, + { 0x100074bb, 0x00 }, + { 0x100074bc, 0x63 }, + { 0x100074bd, 0x04 }, + { 0x100074be, 0x07 }, + { 0x100074bf, 0x04 }, + { 0x100074c0, 0x37 }, + { 0x100074c1, 0x07 }, + { 0x100074c2, 0x00 }, + { 0x100074c3, 0x11 }, + { 0x100074c4, 0x03 }, + { 0x100074c5, 0x47 }, + { 0x100074c6, 0xc7 }, + { 0x100074c7, 0x0e }, + { 0x100074c8, 0x93 }, + { 0x100074c9, 0x06 }, + { 0x100074ca, 0x40 }, + { 0x100074cb, 0x00 }, + { 0x100074cc, 0x13 }, + { 0x100074cd, 0x77 }, + { 0x100074ce, 0xf7 }, + { 0x100074cf, 0x0f }, + { 0x100074d0, 0x63 }, + { 0x100074d1, 0x00 }, + { 0x100074d2, 0xd7 }, + { 0x100074d3, 0x04 }, + { 0x100074d4, 0x93 }, + { 0x100074d5, 0x06 }, + { 0x100074d6, 0x70 }, + { 0x100074d7, 0x00 }, + { 0x100074d8, 0x63 }, + { 0x100074d9, 0x00 }, + { 0x100074da, 0xd7 }, + { 0x100074db, 0x04 }, + { 0x100074dc, 0x63 }, + { 0x100074dd, 0x84 }, + { 0x100074de, 0x07 }, + { 0x100074df, 0x02 }, + { 0x100074e0, 0xb7 }, + { 0x100074e1, 0xd6 }, + { 0x100074e2, 0x00 }, + { 0x100074e3, 0x00 }, + { 0x100074e4, 0x03 }, + { 0x100074e5, 0xc7 }, + { 0x100074e6, 0x56 }, + { 0x100074e7, 0xa4 }, + { 0x100074e8, 0x13 }, + { 0x100074e9, 0x67 }, + { 0x100074ea, 0x07 }, + { 0x100074eb, 0xf8 }, + { 0x100074ec, 0x13 }, + { 0x100074ed, 0x77 }, + { 0x100074ee, 0xf7 }, + { 0x100074ef, 0x0f }, + { 0x100074f0, 0xa3 }, + { 0x100074f1, 0x82 }, + { 0x100074f2, 0xe6 }, + { 0x100074f3, 0xa4 }, + { 0x100074f4, 0x37 }, + { 0x100074f5, 0xc7 }, + { 0x100074f6, 0x00 }, + { 0x100074f7, 0x00 }, + { 0x100074f8, 0x23 }, + { 0x100074f9, 0x02 }, + { 0x100074fa, 0xf7 }, + { 0x100074fb, 0x06 }, + { 0x100074fc, 0xb7 }, + { 0x100074fd, 0x07 }, + { 0x100074fe, 0x00 }, + { 0x100074ff, 0x08 }, + { 0x10007500, 0x23 }, + { 0x10007501, 0xa2 }, + { 0x10007502, 0xf6 }, + { 0x10007503, 0x58 }, + { 0x10007504, 0x67 }, + { 0x10007505, 0x80 }, + { 0x10007506, 0x00 }, + { 0x10007507, 0x00 }, + { 0x10007508, 0x93 }, + { 0x10007509, 0x07 }, + { 0x1000750a, 0x80 }, + { 0x1000750b, 0x08 }, + { 0x1000750c, 0x6f }, + { 0x1000750d, 0xf0 }, + { 0x1000750e, 0x9f }, + { 0x1000750f, 0xf6 }, + { 0x10007510, 0x93 }, + { 0x10007511, 0x07 }, + { 0x10007512, 0x80 }, + { 0x10007513, 0x08 }, + { 0x10007514, 0x6f }, + { 0x10007515, 0xf0 }, + { 0x10007516, 0xdf }, + { 0x10007517, 0xfc }, + { 0x10007518, 0x93 }, + { 0x10007519, 0x07 }, + { 0x1000751a, 0x60 }, + { 0x1000751b, 0x06 }, + { 0x1000751c, 0x6f }, + { 0x1000751d, 0xf0 }, + { 0x1000751e, 0x5f }, + { 0x1000751f, 0xfc }, + { 0x10007520, 0x37 }, + { 0x10007521, 0xd7 }, + { 0x10007522, 0x00 }, + { 0x10007523, 0x00 }, + { 0x10007524, 0x83 }, + { 0x10007525, 0x27 }, + { 0x10007526, 0x07 }, + { 0x10007527, 0x53 }, + { 0x10007528, 0xb7 }, + { 0x10007529, 0x06 }, + { 0x1000752a, 0x02 }, + { 0x1000752b, 0x00 }, + { 0x1000752c, 0xb3 }, + { 0x1000752d, 0xf7 }, + { 0x1000752e, 0xd7 }, + { 0x1000752f, 0x00 }, + { 0x10007530, 0x63 }, + { 0x10007531, 0x88 }, + { 0x10007532, 0x07 }, + { 0x10007533, 0x00 }, + { 0x10007534, 0x13 }, + { 0x10007535, 0x06 }, + { 0x10007536, 0xa0 }, + { 0x10007537, 0x05 }, + { 0x10007538, 0x23 }, + { 0x10007539, 0xa8 }, + { 0x1000753a, 0xc1 }, + { 0x1000753b, 0x56 }, + { 0x1000753c, 0x23 }, + { 0x1000753d, 0x28 }, + { 0x1000753e, 0xd7 }, + { 0x1000753f, 0x52 }, + { 0x10007540, 0x67 }, + { 0x10007541, 0x80 }, + { 0x10007542, 0x00 }, + { 0x10007543, 0x00 }, + { 0x10007544, 0x37 }, + { 0x10007545, 0xd7 }, + { 0x10007546, 0x00 }, + { 0x10007547, 0x10 }, + { 0x10007548, 0x83 }, + { 0x10007549, 0x47 }, + { 0x1000754a, 0x07 }, + { 0x1000754b, 0xd9 }, + { 0x1000754c, 0x93 }, + { 0x1000754d, 0x06 }, + { 0x1000754e, 0x20 }, + { 0x1000754f, 0x00 }, + { 0x10007550, 0x93 }, + { 0x10007551, 0xf7 }, + { 0x10007552, 0xf7 }, + { 0x10007553, 0x0f }, + { 0x10007554, 0x63 }, + { 0x10007555, 0x9c }, + { 0x10007556, 0xd7 }, + { 0x10007557, 0x02 }, + { 0x10007558, 0xb7 }, + { 0x10007559, 0xc6 }, + { 0x1000755a, 0x00 }, + { 0x1000755b, 0x00 }, + { 0x1000755c, 0x83 }, + { 0x1000755d, 0xc7 }, + { 0x1000755e, 0x26 }, + { 0x1000755f, 0x04 }, + { 0x10007560, 0x93 }, + { 0x10007561, 0xf7 }, + { 0x10007562, 0xf7 }, + { 0x10007563, 0x07 }, + { 0x10007564, 0x23 }, + { 0x10007565, 0x81 }, + { 0x10007566, 0xf6 }, + { 0x10007567, 0x04 }, + { 0x10007568, 0xb7 }, + { 0x10007569, 0xd6 }, + { 0x1000756a, 0x00 }, + { 0x1000756b, 0x00 }, + { 0x1000756c, 0x83 }, + { 0x1000756d, 0xc7 }, + { 0x1000756e, 0xa6 }, + { 0x1000756f, 0xe1 }, + { 0x10007570, 0x93 }, + { 0x10007571, 0xf7 }, + { 0x10007572, 0xf7 }, + { 0x10007573, 0x07 }, + { 0x10007574, 0x23 }, + { 0x10007575, 0x8d }, + { 0x10007576, 0xf6 }, + { 0x10007577, 0xe0 }, + { 0x10007578, 0x23 }, + { 0x10007579, 0x08 }, + { 0x1000757a, 0x07 }, + { 0x1000757b, 0xd8 }, + { 0x1000757c, 0x83 }, + { 0x1000757d, 0x47 }, + { 0x1000757e, 0x47 }, + { 0x1000757f, 0xd9 }, + { 0x10007580, 0x93 }, + { 0x10007581, 0x87 }, + { 0x10007582, 0x17 }, + { 0x10007583, 0x00 }, + { 0x10007584, 0x93 }, + { 0x10007585, 0xf7 }, + { 0x10007586, 0xf7 }, + { 0x10007587, 0x0f }, + { 0x10007588, 0x23 }, + { 0x10007589, 0x0a }, + { 0x1000758a, 0xf7 }, + { 0x1000758b, 0xd8 }, + { 0x1000758c, 0x67 }, + { 0x1000758d, 0x80 }, + { 0x1000758e, 0x00 }, + { 0x1000758f, 0x00 }, + { 0x10007590, 0xb7 }, + { 0x10007591, 0xd7 }, + { 0x10007592, 0x00 }, + { 0x10007593, 0x00 }, + { 0x10007594, 0x83 }, + { 0x10007595, 0xc7 }, + { 0x10007596, 0x07 }, + { 0x10007597, 0x47 }, + { 0x10007598, 0x93 }, + { 0x10007599, 0xf7 }, + { 0x1000759a, 0x07 }, + { 0x1000759b, 0x01 }, + { 0x1000759c, 0x63 }, + { 0x1000759d, 0x8a }, + { 0x1000759e, 0x07 }, + { 0x1000759f, 0x06 }, + { 0x100075a0, 0x63 }, + { 0x100075a1, 0x02 }, + { 0x100075a2, 0x05 }, + { 0x100075a3, 0x06 }, + { 0x100075a4, 0x37 }, + { 0x100075a5, 0xc7 }, + { 0x100075a6, 0x00 }, + { 0x100075a7, 0x00 }, + { 0x100075a8, 0x83 }, + { 0x100075a9, 0x27 }, + { 0x100075aa, 0xc7 }, + { 0x100075ab, 0x5f }, + { 0x100075ac, 0x23 }, + { 0x100075ad, 0xae }, + { 0x100075ae, 0xf1 }, + { 0x100075af, 0x40 }, + { 0x100075b0, 0xb7 }, + { 0x100075b1, 0x06 }, + { 0x100075b2, 0x00 }, + { 0x100075b3, 0x10 }, + { 0x100075b4, 0xb3 }, + { 0x100075b5, 0xf7 }, + { 0x100075b6, 0xd7 }, + { 0x100075b7, 0x00 }, + { 0x100075b8, 0x63 }, + { 0x100075b9, 0x8c }, + { 0x100075ba, 0x07 }, + { 0x100075bb, 0x04 }, + { 0x100075bc, 0x83 }, + { 0x100075bd, 0x47 }, + { 0x100075be, 0x07 }, + { 0x100075bf, 0x56 }, + { 0x100075c0, 0x93 }, + { 0x100075c1, 0xf7 }, + { 0x100075c2, 0x87 }, + { 0x100075c3, 0x01 }, + { 0x100075c4, 0x63 }, + { 0x100075c5, 0x86 }, + { 0x100075c6, 0x07 }, + { 0x100075c7, 0x04 }, + { 0x100075c8, 0x83 }, + { 0x100075c9, 0x47 }, + { 0x100075ca, 0x17 }, + { 0x100075cb, 0x08 }, + { 0x100075cc, 0x93 }, + { 0x100075cd, 0xf7 }, + { 0x100075ce, 0x47 }, + { 0x100075cf, 0x00 }, + { 0x100075d0, 0x63 }, + { 0x100075d1, 0x80 }, + { 0x100075d2, 0x07 }, + { 0x100075d3, 0x04 }, + { 0x100075d4, 0xb7 }, + { 0x100075d5, 0xc7 }, + { 0x100075d6, 0xc2 }, + { 0x100075d7, 0x3f }, + { 0x100075d8, 0x93 }, + { 0x100075d9, 0x87 }, + { 0x100075da, 0x07 }, + { 0x100075db, 0xfc }, + { 0x100075dc, 0x83 }, + { 0x100075dd, 0xa7 }, + { 0x100075de, 0x47 }, + { 0x100075df, 0x00 }, + { 0x100075e0, 0x93 }, + { 0x100075e1, 0xd7 }, + { 0x100075e2, 0x17 }, + { 0x100075e3, 0x00 }, + { 0x100075e4, 0x93 }, + { 0x100075e5, 0xf7 }, + { 0x100075e6, 0x17 }, + { 0x100075e7, 0x00 }, + { 0x100075e8, 0x63 }, + { 0x100075e9, 0x84 }, + { 0x100075ea, 0x07 }, + { 0x100075eb, 0x02 }, + { 0x100075ec, 0x23 }, + { 0x100075ed, 0x8a }, + { 0x100075ee, 0xf1 }, + { 0x100075ef, 0x40 }, + { 0x100075f0, 0xb7 }, + { 0x100075f1, 0x07 }, + { 0x100075f2, 0x00 }, + { 0x100075f3, 0xc0 }, + { 0x100075f4, 0x37 }, + { 0x100075f5, 0xf7 }, + { 0x100075f6, 0x00 }, + { 0x100075f7, 0x00 }, + { 0x100075f8, 0x93 }, + { 0x100075f9, 0x87 }, + { 0x100075fa, 0xf7 }, + { 0x100075fb, 0xff }, + { 0x100075fc, 0x23 }, + { 0x100075fd, 0x2c }, + { 0x100075fe, 0xf7 }, + { 0x100075ff, 0x06 }, + { 0x10007600, 0x67 }, + { 0x10007601, 0x80 }, + { 0x10007602, 0x00 }, + { 0x10007603, 0x00 }, + { 0x10007604, 0x23 }, + { 0x10007605, 0x8a }, + { 0x10007606, 0x01 }, + { 0x10007607, 0x40 }, + { 0x10007608, 0xb7 }, + { 0x10007609, 0xf7 }, + { 0x1000760a, 0x00 }, + { 0x1000760b, 0x00 }, + { 0x1000760c, 0x23 }, + { 0x1000760d, 0xac }, + { 0x1000760e, 0x07 }, + { 0x1000760f, 0x06 }, + { 0x10007610, 0x67 }, + { 0x10007611, 0x80 }, + { 0x10007612, 0x00 }, + { 0x10007613, 0x00 }, + { 0x10007614, 0x13 }, + { 0x10007615, 0x01 }, + { 0x10007616, 0x01 }, + { 0x10007617, 0xff }, + { 0x10007618, 0x23 }, + { 0x10007619, 0x26 }, + { 0x1000761a, 0x11 }, + { 0x1000761b, 0x00 }, + { 0x1000761c, 0x23 }, + { 0x1000761d, 0x24 }, + { 0x1000761e, 0x81 }, + { 0x1000761f, 0x00 }, + { 0x10007620, 0x37 }, + { 0x10007621, 0xc7 }, + { 0x10007622, 0x00 }, + { 0x10007623, 0x00 }, + { 0x10007624, 0x83 }, + { 0x10007625, 0x47 }, + { 0x10007626, 0x07 }, + { 0x10007627, 0x56 }, + { 0x10007628, 0x93 }, + { 0x10007629, 0xf7 }, + { 0x1000762a, 0x17 }, + { 0x1000762b, 0x00 }, + { 0x1000762c, 0x63 }, + { 0x1000762d, 0x98 }, + { 0x1000762e, 0x07 }, + { 0x1000762f, 0x00 }, + { 0x10007630, 0x83 }, + { 0x10007631, 0x47 }, + { 0x10007632, 0x07 }, + { 0x10007633, 0x56 }, + { 0x10007634, 0x93 }, + { 0x10007635, 0xf7 }, + { 0x10007636, 0x27 }, + { 0x10007637, 0x00 }, + { 0x10007638, 0x63 }, + { 0x10007639, 0x82 }, + { 0x1000763a, 0x07 }, + { 0x1000763b, 0x08 }, + { 0x1000763c, 0x37 }, + { 0x1000763d, 0xd4 }, + { 0x1000763e, 0x00 }, + { 0x1000763f, 0x00 }, + { 0x10007640, 0x83 }, + { 0x10007641, 0x47 }, + { 0x10007642, 0x14 }, + { 0x10007643, 0x47 }, + { 0x10007644, 0x93 }, + { 0x10007645, 0xf7 }, + { 0x10007646, 0x27 }, + { 0x10007647, 0x00 }, + { 0x10007648, 0x63 }, + { 0x10007649, 0x8a }, + { 0x1000764a, 0x07 }, + { 0x1000764b, 0x06 }, + { 0x1000764c, 0x93 }, + { 0x1000764d, 0x05 }, + { 0x1000764e, 0x10 }, + { 0x1000764f, 0x00 }, + { 0x10007650, 0x13 }, + { 0x10007651, 0x05 }, + { 0x10007652, 0x20 }, + { 0x10007653, 0x10 }, + { 0x10007654, 0xef }, + { 0x10007655, 0xa0 }, + { 0x10007656, 0x8f }, + { 0x10007657, 0x9a }, + { 0x10007658, 0x37 }, + { 0x10007659, 0x05 }, + { 0x1000765a, 0x01 }, + { 0x1000765b, 0x00 }, + { 0x1000765c, 0x93 }, + { 0x1000765d, 0x05 }, + { 0x1000765e, 0x00 }, + { 0x1000765f, 0x01 }, + { 0x10007660, 0x13 }, + { 0x10007661, 0x05 }, + { 0x10007662, 0xb5 }, + { 0x10007663, 0xa0 }, + { 0x10007664, 0xef }, + { 0x10007665, 0xa0 }, + { 0x10007666, 0x8f }, + { 0x10007667, 0x99 }, + { 0x10007668, 0x83 }, + { 0x10007669, 0x47 }, + { 0x1000766a, 0x24 }, + { 0x1000766b, 0xe0 }, + { 0x1000766c, 0x13 }, + { 0x1000766d, 0x05 }, + { 0x1000766e, 0x80 }, + { 0x1000766f, 0x3e }, + { 0x10007670, 0x93 }, + { 0x10007671, 0x05 }, + { 0x10007672, 0x00 }, + { 0x10007673, 0x00 }, + { 0x10007674, 0x93 }, + { 0x10007675, 0xe7 }, + { 0x10007676, 0x07 }, + { 0x10007677, 0xf8 }, + { 0x10007678, 0x93 }, + { 0x10007679, 0xf7 }, + { 0x1000767a, 0xf7 }, + { 0x1000767b, 0x0f }, + { 0x1000767c, 0x23 }, + { 0x1000767d, 0x01 }, + { 0x1000767e, 0xf4 }, + { 0x1000767f, 0xe0 }, + { 0x10007680, 0x83 }, + { 0x10007681, 0x47 }, + { 0x10007682, 0x24 }, + { 0x10007683, 0xe0 }, + { 0x10007684, 0x93 }, + { 0x10007685, 0xf7 }, + { 0x10007686, 0xf7 }, + { 0x10007687, 0x0f }, + { 0x10007688, 0x93 }, + { 0x10007689, 0xe7 }, + { 0x1000768a, 0x07 }, + { 0x1000768b, 0x04 }, + { 0x1000768c, 0x23 }, + { 0x1000768d, 0x01 }, + { 0x1000768e, 0xf4 }, + { 0x1000768f, 0xe0 }, + { 0x10007690, 0xef }, + { 0x10007691, 0xe0 }, + { 0x10007692, 0x8f }, + { 0x10007693, 0xb9 }, + { 0x10007694, 0x83 }, + { 0x10007695, 0x47 }, + { 0x10007696, 0x34 }, + { 0x10007697, 0xe0 }, + { 0x10007698, 0x93 }, + { 0x10007699, 0xf7 }, + { 0x1000769a, 0x07 }, + { 0x1000769b, 0x02 }, + { 0x1000769c, 0xe3 }, + { 0x1000769d, 0x9c }, + { 0x1000769e, 0x07 }, + { 0x1000769f, 0xfe }, + { 0x100076a0, 0x37 }, + { 0x100076a1, 0x05 }, + { 0x100076a2, 0x01 }, + { 0x100076a3, 0x00 }, + { 0x100076a4, 0x93 }, + { 0x100076a5, 0x05 }, + { 0x100076a6, 0x00 }, + { 0x100076a7, 0x00 }, + { 0x100076a8, 0x13 }, + { 0x100076a9, 0x05 }, + { 0x100076aa, 0xb5 }, + { 0x100076ab, 0xa0 }, + { 0x100076ac, 0xef }, + { 0x100076ad, 0xa0 }, + { 0x100076ae, 0x0f }, + { 0x100076af, 0x95 }, + { 0x100076b0, 0x83 }, + { 0x100076b1, 0x47 }, + { 0x100076b2, 0x14 }, + { 0x100076b3, 0x47 }, + { 0x100076b4, 0x93 }, + { 0x100076b5, 0xf7 }, + { 0x100076b6, 0xd7 }, + { 0x100076b7, 0x0f }, + { 0x100076b8, 0xa3 }, + { 0x100076b9, 0x08 }, + { 0x100076ba, 0xf4 }, + { 0x100076bb, 0x46 }, + { 0x100076bc, 0x03 }, + { 0x100076bd, 0xa7 }, + { 0x100076be, 0x01 }, + { 0x100076bf, 0x57 }, + { 0x100076c0, 0x93 }, + { 0x100076c1, 0x07 }, + { 0x100076c2, 0xa0 }, + { 0x100076c3, 0x05 }, + { 0x100076c4, 0x63 }, + { 0x100076c5, 0x14 }, + { 0x100076c6, 0xf7 }, + { 0x100076c7, 0x04 }, + { 0x100076c8, 0x37 }, + { 0x100076c9, 0x07 }, + { 0x100076ca, 0x00 }, + { 0x100076cb, 0x11 }, + { 0x100076cc, 0x83 }, + { 0x100076cd, 0x47 }, + { 0x100076ce, 0x07 }, + { 0x100076cf, 0x01 }, + { 0x100076d0, 0x13 }, + { 0x100076d1, 0x06 }, + { 0x100076d2, 0x30 }, + { 0x100076d3, 0x00 }, + { 0x100076d4, 0x93 }, + { 0x100076d5, 0xf7 }, + { 0x100076d6, 0xf7 }, + { 0x100076d7, 0x0f }, + { 0x100076d8, 0x63 }, + { 0x100076d9, 0x9a }, + { 0x100076da, 0xc7 }, + { 0x100076db, 0x02 }, + { 0x100076dc, 0x03 }, + { 0x100076dd, 0x47 }, + { 0x100076de, 0x87 }, + { 0x100076df, 0x01 }, + { 0x100076e0, 0x13 }, + { 0x100076e1, 0x77 }, + { 0x100076e2, 0xf7 }, + { 0x100076e3, 0x0f }, + { 0x100076e4, 0x63 }, + { 0x100076e5, 0x14 }, + { 0x100076e6, 0xf7 }, + { 0x100076e7, 0x02 }, + { 0x100076e8, 0x37 }, + { 0x100076e9, 0xd7 }, + { 0x100076ea, 0x00 }, + { 0x100076eb, 0x00 }, + { 0x100076ec, 0x83 }, + { 0x100076ed, 0x47 }, + { 0x100076ee, 0x37 }, + { 0x100076ef, 0x54 }, + { 0x100076f0, 0x93 }, + { 0x100076f1, 0xf7 }, + { 0x100076f2, 0xf7 }, + { 0x100076f3, 0x0f }, + { 0x100076f4, 0x93 }, + { 0x100076f5, 0xe7 }, + { 0x100076f6, 0x07 }, + { 0x100076f7, 0x02 }, + { 0x100076f8, 0xa3 }, + { 0x100076f9, 0x01 }, + { 0x100076fa, 0xf7 }, + { 0x100076fb, 0x54 }, + { 0x100076fc, 0x83 }, + { 0x100076fd, 0x47 }, + { 0x100076fe, 0x37 }, + { 0x100076ff, 0x54 }, + { 0x10007700, 0x93 }, + { 0x10007701, 0xf7 }, + { 0x10007702, 0xf7 }, + { 0x10007703, 0x0d }, + { 0x10007704, 0xa3 }, + { 0x10007705, 0x01 }, + { 0x10007706, 0xf7 }, + { 0x10007707, 0x54 }, + { 0x10007708, 0x23 }, + { 0x10007709, 0xa8 }, + { 0x1000770a, 0x01 }, + { 0x1000770b, 0x56 }, + { 0x1000770c, 0xb7 }, + { 0x1000770d, 0xd7 }, + { 0x1000770e, 0x00 }, + { 0x1000770f, 0x10 }, + { 0x10007710, 0x03 }, + { 0x10007711, 0xc7 }, + { 0x10007712, 0x07 }, + { 0x10007713, 0xd9 }, + { 0x10007714, 0x93 }, + { 0x10007715, 0x06 }, + { 0x10007716, 0x10 }, + { 0x10007717, 0x00 }, + { 0x10007718, 0x13 }, + { 0x10007719, 0x77 }, + { 0x1000771a, 0xf7 }, + { 0x1000771b, 0x0f }, + { 0x1000771c, 0x63 }, + { 0x1000771d, 0x1a }, + { 0x1000771e, 0xd7 }, + { 0x1000771f, 0x04 }, + { 0x10007720, 0x03 }, + { 0x10007721, 0xc7 }, + { 0x10007722, 0x27 }, + { 0x10007723, 0xd9 }, + { 0x10007724, 0x13 }, + { 0x10007725, 0x07 }, + { 0x10007726, 0x17 }, + { 0x10007727, 0x00 }, + { 0x10007728, 0x13 }, + { 0x10007729, 0x77 }, + { 0x1000772a, 0xf7 }, + { 0x1000772b, 0x0f }, + { 0x1000772c, 0x23 }, + { 0x1000772d, 0x89 }, + { 0x1000772e, 0xe7 }, + { 0x1000772f, 0xd8 }, + { 0x10007730, 0x83 }, + { 0x10007731, 0xc6 }, + { 0x10007732, 0x27 }, + { 0x10007733, 0xd9 }, + { 0x10007734, 0x03 }, + { 0x10007735, 0xc7 }, + { 0x10007736, 0x17 }, + { 0x10007737, 0xd9 }, + { 0x10007738, 0x93 }, + { 0x10007739, 0xf6 }, + { 0x1000773a, 0xf6 }, + { 0x1000773b, 0x0f }, + { 0x1000773c, 0x13 }, + { 0x1000773d, 0x77 }, + { 0x1000773e, 0xf7 }, + { 0x1000773f, 0x0f }, + { 0x10007740, 0x63 }, + { 0x10007741, 0xe8 }, + { 0x10007742, 0xe6 }, + { 0x10007743, 0x02 }, + { 0x10007744, 0xb7 }, + { 0x10007745, 0xd6 }, + { 0x10007746, 0x00 }, + { 0x10007747, 0x00 }, + { 0x10007748, 0x03 }, + { 0x10007749, 0xc7 }, + { 0x1000774a, 0xa6 }, + { 0x1000774b, 0xe1 }, + { 0x1000774c, 0x13 }, + { 0x1000774d, 0x67 }, + { 0x1000774e, 0x07 }, + { 0x1000774f, 0xf8 }, + { 0x10007750, 0x13 }, + { 0x10007751, 0x77 }, + { 0x10007752, 0xf7 }, + { 0x10007753, 0x0f }, + { 0x10007754, 0x23 }, + { 0x10007755, 0x8d }, + { 0x10007756, 0xe6 }, + { 0x10007757, 0xe0 }, + { 0x10007758, 0x03 }, + { 0x10007759, 0xc7 }, + { 0x1000775a, 0x37 }, + { 0x1000775b, 0xd9 }, + { 0x1000775c, 0x13 }, + { 0x1000775d, 0x07 }, + { 0x1000775e, 0x17 }, + { 0x1000775f, 0x00 }, + { 0x10007760, 0x13 }, + { 0x10007761, 0x77 }, + { 0x10007762, 0xf7 }, + { 0x10007763, 0x0f }, + { 0x10007764, 0xa3 }, + { 0x10007765, 0x89 }, + { 0x10007766, 0xe7 }, + { 0x10007767, 0xd8 }, + { 0x10007768, 0x13 }, + { 0x10007769, 0x07 }, + { 0x1000776a, 0x20 }, + { 0x1000776b, 0x00 }, + { 0x1000776c, 0x23 }, + { 0x1000776d, 0x88 }, + { 0x1000776e, 0xe7 }, + { 0x1000776f, 0xd8 }, + { 0x10007770, 0x83 }, + { 0x10007771, 0x20 }, + { 0x10007772, 0xc1 }, + { 0x10007773, 0x00 }, + { 0x10007774, 0x03 }, + { 0x10007775, 0x24 }, + { 0x10007776, 0x81 }, + { 0x10007777, 0x00 }, + { 0x10007778, 0x13 }, + { 0x10007779, 0x01 }, + { 0x1000777a, 0x01 }, + { 0x1000777b, 0x01 }, + { 0x1000777c, 0x67 }, + { 0x1000777d, 0x80 }, + { 0x1000777e, 0x00 }, + { 0x1000777f, 0x00 }, + { 0x10007780, 0x03 }, + { 0x10007781, 0xc7 }, + { 0x10007782, 0xa1 }, + { 0x10007783, 0x40 }, + { 0x10007784, 0x93 }, + { 0x10007785, 0x06 }, + { 0x10007786, 0x10 }, + { 0x10007787, 0x00 }, + { 0x10007788, 0x63 }, + { 0x10007789, 0x16 }, + { 0x1000778a, 0xd7 }, + { 0x1000778b, 0x00 }, + { 0x1000778c, 0xb7 }, + { 0x1000778d, 0xd6 }, + { 0x1000778e, 0x00 }, + { 0x1000778f, 0x10 }, + { 0x10007790, 0xa3 }, + { 0x10007791, 0x8a }, + { 0x10007792, 0xe6 }, + { 0x10007793, 0xd8 }, + { 0x10007794, 0x83 }, + { 0x10007795, 0xc7 }, + { 0x10007796, 0xa1 }, + { 0x10007797, 0x40 }, + { 0x10007798, 0x63 }, + { 0x10007799, 0x9c }, + { 0x1000779a, 0x07 }, + { 0x1000779b, 0x06 }, + { 0x1000779c, 0x13 }, + { 0x1000779d, 0x01 }, + { 0x1000779e, 0x01 }, + { 0x1000779f, 0xff }, + { 0x100077a0, 0x23 }, + { 0x100077a1, 0x22 }, + { 0x100077a2, 0x91 }, + { 0x100077a3, 0x00 }, + { 0x100077a4, 0x23 }, + { 0x100077a5, 0x26 }, + { 0x100077a6, 0x11 }, + { 0x100077a7, 0x00 }, + { 0x100077a8, 0x23 }, + { 0x100077a9, 0x24 }, + { 0x100077aa, 0x81 }, + { 0x100077ab, 0x00 }, + { 0x100077ac, 0xb7 }, + { 0x100077ad, 0xc4 }, + { 0x100077ae, 0x00 }, + { 0x100077af, 0x00 }, + { 0x100077b0, 0x83 }, + { 0x100077b1, 0xc7 }, + { 0x100077b2, 0x04 }, + { 0x100077b3, 0x56 }, + { 0x100077b4, 0x13 }, + { 0x100077b5, 0x07 }, + { 0x100077b6, 0x80 }, + { 0x100077b7, 0x01 }, + { 0x100077b8, 0x93 }, + { 0x100077b9, 0xf7 }, + { 0x100077ba, 0xf7 }, + { 0x100077bb, 0x0f }, + { 0x100077bc, 0x63 }, + { 0x100077bd, 0x70 }, + { 0x100077be, 0xf7 }, + { 0x100077bf, 0x04 }, + { 0x100077c0, 0x37 }, + { 0x100077c1, 0xd4 }, + { 0x100077c2, 0x00 }, + { 0x100077c3, 0x10 }, + { 0x100077c4, 0x83 }, + { 0x100077c5, 0x47 }, + { 0x100077c6, 0x54 }, + { 0x100077c7, 0xd9 }, + { 0x100077c8, 0x93 }, + { 0x100077c9, 0xf7 }, + { 0x100077ca, 0xf7 }, + { 0x100077cb, 0x0f }, + { 0x100077cc, 0x63 }, + { 0x100077cd, 0x88 }, + { 0x100077ce, 0x07 }, + { 0x100077cf, 0x02 }, + { 0x100077d0, 0x93 }, + { 0x100077d1, 0x07 }, + { 0x100077d2, 0x10 }, + { 0x100077d3, 0x00 }, + { 0x100077d4, 0x23 }, + { 0x100077d5, 0x82 }, + { 0x100077d6, 0xf4 }, + { 0x100077d7, 0x58 }, + { 0x100077d8, 0x03 }, + { 0x100077d9, 0x45 }, + { 0x100077da, 0x64 }, + { 0x100077db, 0xd9 }, + { 0x100077dc, 0xb7 }, + { 0x100077dd, 0x15 }, + { 0x100077de, 0x00 }, + { 0x100077df, 0x00 }, + { 0x100077e0, 0x93 }, + { 0x100077e1, 0x85 }, + { 0x100077e2, 0x85 }, + { 0x100077e3, 0x38 }, + { 0x100077e4, 0x13 }, + { 0x100077e5, 0x75 }, + { 0x100077e6, 0xf5 }, + { 0x100077e7, 0x0f }, + { 0x100077e8, 0xef }, + { 0x100077e9, 0xe0 }, + { 0x100077ea, 0x9f }, + { 0x100077eb, 0xd0 }, + { 0x100077ec, 0x93 }, + { 0x100077ed, 0x55 }, + { 0x100077ee, 0xf5 }, + { 0x100077ef, 0x41 }, + { 0x100077f0, 0xef }, + { 0x100077f1, 0xe0 }, + { 0x100077f2, 0x8f }, + { 0x100077f3, 0xa3 }, + { 0x100077f4, 0x23 }, + { 0x100077f5, 0x82 }, + { 0x100077f6, 0x04 }, + { 0x100077f7, 0x58 }, + { 0x100077f8, 0xa3 }, + { 0x100077f9, 0x0a }, + { 0x100077fa, 0x04 }, + { 0x100077fb, 0xd8 }, + { 0x100077fc, 0x83 }, + { 0x100077fd, 0x20 }, + { 0x100077fe, 0xc1 }, + { 0x100077ff, 0x00 }, + { 0x10007800, 0x03 }, + { 0x10007801, 0x24 }, + { 0x10007802, 0x81 }, + { 0x10007803, 0x00 }, + { 0x10007804, 0x83 }, + { 0x10007805, 0x24 }, + { 0x10007806, 0x41 }, + { 0x10007807, 0x00 }, + { 0x10007808, 0x13 }, + { 0x10007809, 0x01 }, + { 0x1000780a, 0x01 }, + { 0x1000780b, 0x01 }, + { 0x1000780c, 0x67 }, + { 0x1000780d, 0x80 }, + { 0x1000780e, 0x00 }, + { 0x1000780f, 0x00 }, + { 0x10007810, 0x67 }, + { 0x10007811, 0x80 }, + { 0x10007812, 0x00 }, + { 0x10007813, 0x00 }, + { 0x10007814, 0x13 }, + { 0x10007815, 0x01 }, + { 0x10007816, 0x01 }, + { 0x10007817, 0xff }, + { 0x10007818, 0x23 }, + { 0x10007819, 0x26 }, + { 0x1000781a, 0x11 }, + { 0x1000781b, 0x00 }, + { 0x1000781c, 0xef }, + { 0x1000781d, 0xd0 }, + { 0x1000781e, 0x8f }, + { 0x1000781f, 0x86 }, + { 0x10007820, 0x83 }, + { 0x10007821, 0xc7 }, + { 0x10007822, 0x11 }, + { 0x10007823, 0x42 }, + { 0x10007824, 0x63 }, + { 0x10007825, 0x86 }, + { 0x10007826, 0x07 }, + { 0x10007827, 0x00 }, + { 0x10007828, 0x03 }, + { 0x10007829, 0xc7 }, + { 0x1000782a, 0x01 }, + { 0x1000782b, 0x42 }, + { 0x1000782c, 0x63 }, + { 0x1000782d, 0x10 }, + { 0x1000782e, 0x07 }, + { 0x1000782f, 0x02 }, + { 0x10007830, 0x83 }, + { 0x10007831, 0xc6 }, + { 0x10007832, 0x21 }, + { 0x10007833, 0x41 }, + { 0x10007834, 0x13 }, + { 0x10007835, 0x07 }, + { 0x10007836, 0xf0 }, + { 0x10007837, 0x01 }, + { 0x10007838, 0x13 }, + { 0x10007839, 0x05 }, + { 0x1000783a, 0xf0 }, + { 0x1000783b, 0x01 }, + { 0x1000783c, 0x63 }, + { 0x1000783d, 0x98 }, + { 0x1000783e, 0xe6 }, + { 0x1000783f, 0x02 }, + { 0x10007840, 0x63 }, + { 0x10007841, 0x8a }, + { 0x10007842, 0x07 }, + { 0x10007843, 0x02 }, + { 0x10007844, 0x83 }, + { 0x10007845, 0xc7 }, + { 0x10007846, 0x01 }, + { 0x10007847, 0x42 }, + { 0x10007848, 0x63 }, + { 0x10007849, 0x86 }, + { 0x1000784a, 0x07 }, + { 0x1000784b, 0x02 }, + { 0x1000784c, 0x83 }, + { 0x1000784d, 0xc7 }, + { 0x1000784e, 0x31 }, + { 0x1000784f, 0x42 }, + { 0x10007850, 0x63 }, + { 0x10007851, 0x86 }, + { 0x10007852, 0x07 }, + { 0x10007853, 0x00 }, + { 0x10007854, 0x83 }, + { 0x10007855, 0xc7 }, + { 0x10007856, 0x21 }, + { 0x10007857, 0x42 }, + { 0x10007858, 0x63 }, + { 0x10007859, 0x9e }, + { 0x1000785a, 0x07 }, + { 0x1000785b, 0x00 }, + { 0x1000785c, 0x03 }, + { 0x1000785d, 0xc7 }, + { 0x1000785e, 0x21 }, + { 0x1000785f, 0x41 }, + { 0x10007860, 0x93 }, + { 0x10007861, 0x07 }, + { 0x10007862, 0xb0 }, + { 0x10007863, 0x01 }, + { 0x10007864, 0x63 }, + { 0x10007865, 0x08 }, + { 0x10007866, 0xf7 }, + { 0x10007867, 0x00 }, + { 0x10007868, 0x13 }, + { 0x10007869, 0x05 }, + { 0x1000786a, 0xb0 }, + { 0x1000786b, 0x01 }, + { 0x1000786c, 0xef }, + { 0x1000786d, 0xd0 }, + { 0x1000786e, 0x0f }, + { 0x1000786f, 0xcf }, + { 0x10007870, 0xef }, + { 0x10007871, 0xd0 }, + { 0x10007872, 0x8f }, + { 0x10007873, 0xa4 }, + { 0x10007874, 0x93 }, + { 0x10007875, 0x06 }, + { 0x10007876, 0x10 }, + { 0x10007877, 0x00 }, + { 0x10007878, 0xa3 }, + { 0x10007879, 0x89 }, + { 0x1000787a, 0xd1 }, + { 0x1000787b, 0x40 }, + { 0x1000787c, 0x37 }, + { 0x1000787d, 0xd7 }, + { 0x1000787e, 0x00 }, + { 0x1000787f, 0x10 }, + { 0x10007880, 0x83 }, + { 0x10007881, 0x47 }, + { 0x10007882, 0x07 }, + { 0x10007883, 0xd9 }, + { 0x10007884, 0x93 }, + { 0x10007885, 0xf7 }, + { 0x10007886, 0xf7 }, + { 0x10007887, 0x0f }, + { 0x10007888, 0x63 }, + { 0x10007889, 0x90 }, + { 0x1000788a, 0x07 }, + { 0x1000788b, 0x02 }, + { 0x1000788c, 0x37 }, + { 0x1000788d, 0xc6 }, + { 0x1000788e, 0x00 }, + { 0x1000788f, 0x00 }, + { 0x10007890, 0x83 }, + { 0x10007891, 0x47 }, + { 0x10007892, 0x26 }, + { 0x10007893, 0x04 }, + { 0x10007894, 0x93 }, + { 0x10007895, 0xe7 }, + { 0x10007896, 0x07 }, + { 0x10007897, 0xf8 }, + { 0x10007898, 0x93 }, + { 0x10007899, 0xf7 }, + { 0x1000789a, 0xf7 }, + { 0x1000789b, 0x0f }, + { 0x1000789c, 0x23 }, + { 0x1000789d, 0x01 }, + { 0x1000789e, 0xf6 }, + { 0x1000789f, 0x04 }, + { 0x100078a0, 0x23 }, + { 0x100078a1, 0x08 }, + { 0x100078a2, 0xd7 }, + { 0x100078a3, 0xd8 }, + { 0x100078a4, 0x23 }, + { 0x100078a5, 0x09 }, + { 0x100078a6, 0x07 }, + { 0x100078a7, 0xd8 }, + { 0x100078a8, 0x83 }, + { 0x100078a9, 0x20 }, + { 0x100078aa, 0xc1 }, + { 0x100078ab, 0x00 }, + { 0x100078ac, 0x13 }, + { 0x100078ad, 0x01 }, + { 0x100078ae, 0x01 }, + { 0x100078af, 0x01 }, + { 0x100078b0, 0x67 }, + { 0x100078b1, 0x80 }, + { 0x100078b2, 0x00 }, + { 0x100078b3, 0x00 }, + { 0x3fc2bfc7, 0x00 }, + { 0x3fc2bfc6, 0x00 }, + { 0x3fc2bfc5, 0x00 }, + { 0x3fc2bfc4, 0x01 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x02 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x11 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x82 }, + { 0x1000db06, 0x04 }, + { 0x1000db07, 0xf1 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0x40 }, + { 0x0000d540, 0x01 }, +}; + +static const struct reg_default rt1320_reg_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x0b }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +static const struct reg_default rt1320_mbq_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, +}; + +static bool rt1320_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000 ... 0xc086: + case 0xc400 ... 0xc409: + case 0xc480 ... 0xc48f: + case 0xc4c0 ... 0xc4c4: + case 0xc4e0 ... 0xc4e7: + case 0xc500: + case 0xc560 ... 0xc56b: + case 0xc570: + case 0xc580 ... 0xc59a: + case 0xc5b0 ... 0xc60f: + case 0xc640 ... 0xc64f: + case 0xc670: + case 0xc680 ... 0xc683: + case 0xc700 ... 0xc76f: + case 0xc800 ... 0xc801: + case 0xc820: + case 0xc900 ... 0xc901: + case 0xc920 ... 0xc921: + case 0xca00 ... 0xca07: + case 0xca20 ... 0xca27: + case 0xca40 ... 0xca4b: + case 0xca60 ... 0xca68: + case 0xca80 ... 0xca88: + case 0xcb00 ... 0xcb0c: + case 0xcc00 ... 0xcc12: + case 0xcc80 ... 0xcc81: + case 0xcd00: + case 0xcd80 ... 0xcd82: + case 0xce00 ... 0xce4d: + case 0xcf00 ... 0xcf25: + case 0xd000 ... 0xd0ff: + case 0xd100 ... 0xd1ff: + case 0xd200 ... 0xd2ff: + case 0xd300 ... 0xd3ff: + case 0xd400 ... 0xd403: + case 0xd410 ... 0xd417: + case 0xd470 ... 0xd497: + case 0xd4dc ... 0xd50f: + case 0xd520 ... 0xd543: + case 0xd560 ... 0xd5ef: + case 0xd600 ... 0xd663: + case 0xda00 ... 0xda6e: + case 0xda80 ... 0xda9e: + case 0xdb00 ... 0xdb7f: + case 0xdc00: + case 0xdc20 ... 0xdc21: + case 0xdd00 ... 0xdd17: + case 0xde00 ... 0xde09: + case 0xdf00 ... 0xdf1b: + case 0xe000 ... 0xe847: + case 0xf717 ... 0xf719: + case 0xf720 ... 0xf723: + case 0x1000f008: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static bool rt1320_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc402 ... 0xc406: + case 0xc48c ... 0xc48f: + case 0xc560: + case 0xc5b5 ... 0xc5b7: + case 0xc5fc ... 0xc5ff: + case 0xc820: + case 0xc900: + case 0xc920: + case 0xca42: + case 0xca62: + case 0xca82: + case 0xcd00: + case 0xce03: + case 0xce10: + case 0xce14 ... 0xce17: + case 0xce44 ... 0xce49: + case 0xce4c ... 0xce4d: + case 0xcf0c: + case 0xcf10 ... 0xcf25: + case 0xd486 ... 0xd487: + case 0xd4e5 ... 0xd4e6: + case 0xd4e8 ... 0xd4ff: + case 0xd530: + case 0xd540: + case 0xd543: + case 0xdb58 ... 0xdb5f: + case 0xdb60 ... 0xdb63: + case 0xdb68 ... 0xdb69: + case 0xdb6d: + case 0xdb70 ... 0xdb71: + case 0xdb76: + case 0xdb7a: + case 0xdb7c ... 0xdb7f: + case 0xdd0c ... 0xdd13: + case 0xde02: + case 0xdf14 ... 0xdf1b: + case 0xe83c ... 0xe847: + case 0xf717 ... 0xf719: + case 0xf720 ... 0xf723: + case 0x10000000 ... 0x10007fff: + case 0x1000c000 ... 0x1000dfff: + case 0x1000f008: + case 0x3fc2bfc4 ... 0x3fc2bfc7: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static bool rt1320_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02): + return true; + default: + return false; + } +} + +static const struct regmap_config rt1320_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1320_readable_register, + .volatile_reg = rt1320_volatile_register, + .max_register = 0x41081488, + .reg_defaults = rt1320_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1320_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt1320_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt1320_mbq_readable_register, + .max_register = 0x41000192, + .reg_defaults = rt1320_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1320_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1320_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + /* + * Due to support the multi-lane, we call 'sdw_slave_read_prop' to get the lane mapping + */ + sdw_slave_read_prop(slave); + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + prop->lane_control_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(4); + prop->sink_ports = BIT(1); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 64; + + return 0; +} + +static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + unsigned int amp_func_status, val, tmp; + + if (rt1320->hw_init) + return 0; + + regcache_cache_only(rt1320->regmap, false); + regcache_cache_only(rt1320->mbq_regmap, false); + if (rt1320->first_hw_init) { + regcache_cache_bypass(rt1320->regmap, true); + regcache_cache_bypass(rt1320->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + if (rt1320->version_id < 0) { + regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val); + rt1320->version_id = val; + } + + regmap_read(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), &_func_status); + dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status); + + /* initialization write */ + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt1320->first_hw_init)) { + regmap_multi_reg_write(rt1320->regmap, rt1320_blind_write, ARRAY_SIZE(rt1320_blind_write)); + regmap_multi_reg_write(rt1320->regmap, rt1320_patch_code_write, + ARRAY_SIZE(rt1320_patch_code_write)); + + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + if (!rt1320->first_hw_init) { + regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0); + regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val); + regmap_read(rt1320->regmap, RT1320_HIFI_VER_1, &tmp); + val = (tmp << 8) | val; + regmap_read(rt1320->regmap, RT1320_HIFI_VER_2, &tmp); + val = (tmp << 16) | val; + regmap_read(rt1320->regmap, RT1320_HIFI_VER_3, &tmp); + val = (tmp << 24) | val; + dev_dbg(dev, "%s ROM version=0x%x\n", __func__, val); + /* + * We call the version b which has the new DSP ROM code against version a. + * Therefore, we read the DSP address to check the ID. + */ + if (val == RT1320_VER_B_ID) + rt1320->version_id = RT1320_VB; + regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3); + } + dev_dbg(dev, "%s version_id=%d\n", __func__, rt1320->version_id); + + if (rt1320->first_hw_init) { + regcache_cache_bypass(rt1320->regmap, false); + regcache_cache_bypass(rt1320->mbq_regmap, false); + regcache_mark_dirty(rt1320->regmap); + regcache_mark_dirty(rt1320->mbq_regmap); + } + + /* Mark Slave initialization complete */ + rt1320->first_hw_init = true; + rt1320->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +static int rt1320_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1320->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1320->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1320_io_init(&slave->dev, slave); +} + +static int rt1320_pde23_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + default: + break; + } + + return 0; +} + +static int rt1320_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + + regmap_read(rt1320->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt1320->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + + if (lvalue == gain_l_val && rvalue == gain_r_val) + return 0; + + /* Lch*/ + regmap_write(rt1320->mbq_regmap, mc->reg, gain_l_val); + /* Rch */ + regmap_write(rt1320->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt1320->mbq_regmap, mc->reg, &read_l); + regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return 1; + + return -EIO; +} + +static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + const unsigned int interval_offset = 0xc0; + + regmap_read(rt1320->mbq_regmap, mc->reg, &read_l); + regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r); + + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + + if (read_l != read_r) + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + else + ctl_r = ctl_l; + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + return 0; +} + +static const char * const rt1320_rx_data_ch_select[] = { + "L,R", + "R,L", + "L,L", + "R,R", + "L,L+R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0, + rt1320_rx_data_ch_select); + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); + +static const struct snd_kcontrol_new rt1320_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), + 0, 0x57, 0, rt1320_set_gain_get, rt1320_set_gain_put, out_vol_tlv), + SOC_ENUM("RX Channel Select", rt1320_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1320_spk_l_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), + 0, 1, 1); +static const struct snd_kcontrol_new rt1320_spk_r_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_PGA("FU21", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt1320_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Output */ + SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt1320_spk_l_dac), + SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt1320_spk_r_dac), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + + /* Input */ + SND_SOC_DAPM_PGA("AEC Data", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("AEC Gen"), +}; + +static const struct snd_soc_dapm_route rt1320_dapm_routes[] = { + { "FU21", NULL, "DP1RX" }, + { "FU21", NULL, "PDE 23" }, + { "OT23 L", "Switch", "FU21" }, + { "OT23 R", "Switch", "FU21" }, + { "SPOL", NULL, "OT23 L" }, + { "SPOR", NULL, "OT23 R" }, + + { "AEC Data", NULL, "AEC Gen" }, + { "DP4TX", NULL, "AEC Data" }, +}; + +static int rt1320_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + return 0; +} + +static void rt1320_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1320->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (dai->id == RT1320_AIF1) + port_config.num = 1; + else + return -EINVAL; + } else { + if (dai->id == RT1320_AIF1) + port_config.num = 4; + else + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 16000: + sampling_rate = RT1320_SDCA_RATE_16000HZ; + break; + case 32000: + sampling_rate = RT1320_SDCA_RATE_32000HZ; + break; + case 44100: + sampling_rate = RT1320_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1320_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1320_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1320_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1320->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1320->sdw_slave, sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt1320_slave_ops = { + .read_prop = rt1320_read_prop, + .update_status = rt1320_update_status, +}; + +static int rt1320_sdw_component_probe(struct snd_soc_component *component) +{ + int ret; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); + + rt1320->component = component; + + if (!rt1320->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + dev_dbg(&rt1320->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_component_sdw_rt1320 = { + .probe = rt1320_sdw_component_probe, + .controls = rt1320_snd_controls, + .num_controls = ARRAY_SIZE(rt1320_snd_controls), + .dapm_widgets = rt1320_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1320_dapm_widgets), + .dapm_routes = rt1320_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1320_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt1320_aif_dai_ops = { + .hw_params = rt1320_sdw_hw_params, + .hw_free = rt1320_sdw_pcm_hw_free, + .set_stream = rt1320_set_sdw_stream, + .shutdown = rt1320_sdw_shutdown, +}; + +#define RT1320_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver rt1320_sdw_dai[] = { + { + .name = "rt1320-aif1", + .id = RT1320_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT1320_STEREO_RATES, + .formats = RT1320_FORMATS, + }, + .ops = &rt1320_aif_dai_ops, + }, +}; + +static int rt1320_sdw_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt1320_sdw_priv *rt1320; + int ret; + + rt1320 = devm_kzalloc(dev, sizeof(*rt1320), GFP_KERNEL); + if (!rt1320) + return -ENOMEM; + + dev_set_drvdata(dev, rt1320); + rt1320->sdw_slave = slave; + rt1320->mbq_regmap = mbq_regmap; + rt1320->regmap = regmap; + + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1320->hw_init = false; + rt1320->first_hw_init = false; + rt1320->version_id = -1; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1320, + rt1320_sdw_dai, + ARRAY_SIZE(rt1320_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +static int rt1320_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt1320_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt1320_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt1320_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +/* + * Version A/B will use the class id 0 + * The newer version than A/B will use the class id 1, so add it in advance + */ +static const struct sdw_device_id rt1320_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1320_id); + +static int __maybe_unused rt1320_dev_suspend(struct device *dev) +{ + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + + if (!rt1320->hw_init) + return 0; + + regcache_cache_only(rt1320->regmap, true); + regcache_cache_only(rt1320->mbq_regmap, true); + return 0; +} + +#define RT1320_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1320_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1320->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1320_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1320->regmap, false); + regcache_sync(rt1320->regmap); + regcache_cache_only(rt1320->mbq_regmap, false); + regcache_sync(rt1320->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt1320_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume) + SET_RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL) +}; + +static struct sdw_driver rt1320_sdw_driver = { + .driver = { + .name = "rt1320-sdca", + .pm = &rt1320_pm, + }, + .probe = rt1320_sdw_probe, + .remove = rt1320_sdw_remove, + .ops = &rt1320_slave_ops, + .id_table = rt1320_id, +}; +module_sdw_driver(rt1320_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h new file mode 100644 index 000000000000..b23228e74568 --- /dev/null +++ b/sound/soc/codecs/rt1320-sdw.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1320-sdw.h -- RT1320 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + */ + +#ifndef __RT1320_SDW_H__ +#define __RT1320_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/soc.h> + +/* imp-defined registers */ +#define RT1320_DEV_VERSION_ID_1 0xc404 + +#define RT1320_KR0_STATUS_CNT 0x1000f008 +#define RT1320_HIFI_VER_0 0x3fe2e000 +#define RT1320_HIFI_VER_1 0x3fe2e001 +#define RT1320_HIFI_VER_2 0x3fe2e002 +#define RT1320_HIFI_VER_3 0x3fe2e003 + +/* RT1320 SDCA Control - function number */ +#define FUNC_NUM_AMP 0x04 + +/* RT1320 SDCA entity */ +#define RT1320_SDCA_ENT0 0x00 +#define RT1320_SDCA_ENT_PDE11 0x2a +#define RT1320_SDCA_ENT_PDE23 0x33 +#define RT1320_SDCA_ENT_PDE27 0x27 +#define RT1320_SDCA_ENT_FU14 0x32 +#define RT1320_SDCA_ENT_FU21 0x03 +#define RT1320_SDCA_ENT_FU113 0x30 +#define RT1320_SDCA_ENT_CS14 0x13 +#define RT1320_SDCA_ENT_CS21 0x21 +#define RT1320_SDCA_ENT_CS113 0x12 +#define RT1320_SDCA_ENT_SAPU 0x29 +#define RT1320_SDCA_ENT_PPU21 0x04 + +/* RT1320 SDCA control */ +#define RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT1320_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1320_SDCA_CTL_FU_MUTE 0x01 +#define RT1320_SDCA_CTL_FU_VOLUME 0x02 +#define RT1320_SDCA_CTL_SAPU_PROTECTION_MODE 0x10 +#define RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11 +#define RT1320_SDCA_CTL_POSTURE_NUMBER 0x10 +#define RT1320_SDCA_CTL_FUNC_STATUS 0x10 + +/* RT1320 SDCA channel */ +#define CH_01 0x01 +#define CH_02 0x02 + +/* Function_Status */ +#define FUNCTION_NEEDS_INITIALIZATION BIT(5) + +/* Sample Frequency Index */ +#define RT1320_SDCA_RATE_16000HZ 0x04 +#define RT1320_SDCA_RATE_32000HZ 0x07 +#define RT1320_SDCA_RATE_44100HZ 0x08 +#define RT1320_SDCA_RATE_48000HZ 0x09 +#define RT1320_SDCA_RATE_96000HZ 0x0b +#define RT1320_SDCA_RATE_192000HZ 0x0d + +enum { + RT1320_AIF1, +}; + +/* + * The version id will be useful to distinguish the capability between the different IC versions. + * Currently, VA and VB have different DSP FW versions. + */ +enum rt1320_version_id { + RT1320_VA, + RT1320_VB, +}; + +#define RT1320_VER_B_ID 0x07392238 + +struct rt1320_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regmap *mbq_regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + int version_id; +}; + +#endif /* __RT1320_SDW_H__ */ diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 1e8dbfc3ecd9..dd6ccf17afd4 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -81,6 +81,24 @@ static void rt711_sdca_reset(struct rt711_sdca_priv *rt711) RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1); } +static void rt711_sdca_ge_force_jack_type(struct rt711_sdca_priv *rt711, unsigned int det_mode) +{ + switch (det_mode) { + case 0x00: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x0000); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x10, 0x00); + break; + case 0x03: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8000); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x13); + break; + case 0x05: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8400); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x15); + break; + } +} + static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711) { unsigned int val, loop_rc = 0, loop_dc = 0; @@ -248,6 +266,8 @@ static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711) unsigned int det_mode; int ret; + rt711_sdca_ge_force_jack_type(rt711, rt711->ge_mode_override); + /* get detected_mode */ ret = regmap_read(rt711->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0), @@ -790,6 +810,56 @@ static int rt711_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, return changed; } +static int rt711_sdca_ge_select_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val, item; + + val = (rt711->ge_mode_override >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + return 0; +} + +static int rt711_sdca_ge_select_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val, change = 0; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + if (rt711->ge_mode_override != val) { + rt711->ge_mode_override = val; + change = 1; + } + + return change; +} + +static const char * const rt711_sdca_ge_select[] = { + "Auto", + "Headphone", + "Headset", +}; + +static int rt711_sdca_ge_select_values[] = { + 0, + 3, + 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt711_sdca_ge_mode_enum, SND_SOC_NOPM, + 0, 0x7, rt711_sdca_ge_select, rt711_sdca_ge_select_values); + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); @@ -824,6 +894,8 @@ static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv), + SOC_ENUM_EXT("GE49 Selected Mode", rt711_sdca_ge_mode_enum, + rt711_sdca_ge_select_get, rt711_sdca_ge_select_put), }; static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 11d421e8ab2b..15263dcb0314 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -33,6 +33,7 @@ struct rt711_sdca_priv { int hw_ver; bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute; bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute; + unsigned int ge_mode_override; }; /* NID */ diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index ce337db86d1a..90d5aaddbd5b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -34,6 +34,10 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: return true; default: @@ -56,6 +60,10 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0): case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: return true; default: @@ -78,13 +86,21 @@ static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x5c00000 ... 0x5c0009a: case 0x5d00000 ... 0x5d00009: case 0x5f00000 ... 0x5f00030: - case 0x6100000 ... 0x6100068: - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R): - case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L): - case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case 0x6100000 ... 0x61000f1: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04): return true; default: return false; @@ -96,7 +112,9 @@ static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int re switch (reg) { case 0x2000000: case 0x200001a: + case 0x2000020: case 0x2000024: + case 0x2000030: case 0x2000046: case 0x200008a: case 0x5800000: @@ -178,13 +196,15 @@ static int rt712_sdca_read_prop(struct sdw_slave *slave) unsigned long addr; struct sdw_dpn_prop *dpn; + sdw_slave_read_prop(slave); + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; prop->paging_support = true; /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = BIT(4); /* BITMAP: 00010000 */ + prop->source_ports = BIT(8) | BIT(4); /* BITMAP: 100010000 */ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ nval = hweight32(prop->source_ports); diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index 4be22ccd8561..99fd2d67f04d 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -12,68 +12,31 @@ #include <linux/soundwire/sdw_registers.h> static const struct reg_default rt712_sdca_reg_defaults[] = { - { 0x201a, 0x00 }, - { 0x201b, 0x00 }, - { 0x201c, 0x00 }, - { 0x201d, 0x00 }, - { 0x201e, 0x00 }, - { 0x201f, 0x00 }, - { 0x2029, 0x00 }, - { 0x202a, 0x00 }, - { 0x202d, 0x00 }, - { 0x202e, 0x00 }, - { 0x202f, 0x00 }, - { 0x2030, 0x00 }, - { 0x2031, 0x00 }, - { 0x2032, 0x00 }, - { 0x2033, 0x00 }, - { 0x2034, 0x00 }, - { 0x2230, 0x00 }, - { 0x2231, 0x2f }, - { 0x2232, 0x80 }, - { 0x2f01, 0x00 }, - { 0x2f02, 0x09 }, - { 0x2f03, 0x00 }, - { 0x2f04, 0x00 }, - { 0x2f05, 0x0b }, - { 0x2f06, 0x01 }, - { 0x2f08, 0x00 }, - { 0x2f09, 0x00 }, - { 0x2f0a, 0x01 }, - { 0x2f35, 0x01 }, - { 0x2f36, 0xcf }, - { 0x2f50, 0x0f }, - { 0x2f54, 0x01 }, - { 0x2f58, 0x07 }, - { 0x2f59, 0x09 }, - { 0x2f5a, 0x01 }, - { 0x2f5b, 0x07 }, - { 0x2f5c, 0x05 }, - { 0x2f5d, 0x05 }, - { 0x3201, 0x01 }, - { 0x320c, 0x00 }, - { 0x3301, 0x01 }, - { 0x3302, 0x00 }, - { 0x3303, 0x1f }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x2000004, 0xaa01 }, { 0x200000e, 0x21e0 }, - { 0x2000024, 0x01ba }, { 0x200004a, 0x8830 }, { 0x2000067, 0xf100 }, { 0x5800000, 0x1893 }, @@ -81,12 +44,8 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x5b00005, 0x0000 }, { 0x5b00029, 0x3fff }, { 0x5b0002a, 0xf000 }, - { 0x5f00008, 0x7000 }, + { 0x6100000, 0x04e4 }, { 0x610000e, 0x0007 }, - { 0x6100022, 0x2828 }, - { 0x6100023, 0x2929 }, - { 0x6100026, 0x2c29 }, - { 0x610002c, 0x4150 }, { 0x6100045, 0x0860 }, { 0x6100046, 0x0029 }, { 0x6100053, 0x3fff }, @@ -95,14 +54,22 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x6100064, 0x8000 }, { 0x6100065, 0x0000 }, { 0x6100067, 0xff12 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, }; #endif /* __RT712_SDW_H__ */ diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index b503de9fda80..e210c574bb74 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -82,7 +82,8 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) dev = regmap_get_device(regmap); /* Set HP-JD source from JD1 */ - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + if (rt712->version_id == RT712_VA) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); /* FSM switch to calibration manual mode */ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); @@ -198,11 +199,17 @@ static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712) _end_btn_det_: /* Host is owner, so set back to device */ - if (owner == 0) + if (owner == 0) { /* set owner to device */ - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, - RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + if (rt712->version_id == RT712_VA) + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + else + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + } return btn_type; } @@ -415,8 +422,9 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) switch (rt712->jd_src) { case RT712_JD1: - /* Set HP-JD source from JD1 */ - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + /* Set HP-JD source from JD1, VB uses JD1 in default */ + if (rt712->version_id == RT712_VA) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); break; default: dev_warn(rt712->component->dev, "Wrong JD source\n"); @@ -592,20 +600,20 @@ static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol, static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712) { int err; - unsigned int ch_l, ch_r; + unsigned int ch_01, ch_02; - ch_l = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; - ch_r = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; + ch_01 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; err = regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, - RT712_SDCA_CTL_FU_MUTE, CH_L), ch_l); + RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01); if (err < 0) return err; err = regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, - RT712_SDCA_CTL_FU_MUTE, CH_R), ch_r); + RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02); if (err < 0) return err; @@ -649,28 +657,28 @@ static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); static const struct snd_kcontrol_new rt712_sdca_controls[] = { SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x57, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put), SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x3f, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv), SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume", - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 8, 3, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv), }; static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = { SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0, 0x57, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), }; @@ -763,21 +771,21 @@ static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_L), + RT712_SDCA_CTL_FU_MUTE, CH_01), unmute); regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_R), + RT712_SDCA_CTL_FU_MUTE, CH_02), unmute); break; case SND_SOC_DAPM_PRE_PMD: regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_L), + RT712_SDCA_CTL_FU_MUTE, CH_01), mute); regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_R), + RT712_SDCA_CTL_FU_MUTE, CH_02), mute); break; } @@ -854,7 +862,7 @@ static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w, return 0; } -static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, +static int rt712_sdca_pde23_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = @@ -883,10 +891,13 @@ static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_kcontrol_new rt712_spk_sto_dac = - SOC_DAPM_DOUBLE_R("Switch", - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), - SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), +static const struct snd_kcontrol_new rt712_spk_l_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), + 0, 1, 1); +static const struct snd_kcontrol_new rt712_spk_r_dac = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0, 1, 1); static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = { @@ -931,20 +942,26 @@ static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), /* Digital Interface */ - SND_SOC_DAPM_SWITCH("FU06", SND_SOC_NOPM, 0, 0, &rt712_spk_sto_dac), + SND_SOC_DAPM_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), /* Output */ - SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, - rt712_sdca_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt712_spk_l_dac), + SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt712_spk_r_dac), SND_SOC_DAPM_OUTPUT("SPOL"), SND_SOC_DAPM_OUTPUT("SPOR"), }; static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = { - { "FU06", "Switch", "DP3RX" }, - { "CLASS D", NULL, "FU06" }, - { "SPOL", NULL, "CLASS D" }, - { "SPOR", NULL, "CLASS D" }, + { "FU06", NULL, "DP3RX" }, + { "FU06", NULL, "PDE 23" }, + { "OT23 L", "Switch", "FU06" }, + { "OT23 R", "Switch", "FU06" }, + { "SPOL", NULL, "OT23 L" }, + { "SPOR", NULL, "OT23 R" }, }; static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev) @@ -983,6 +1000,376 @@ static int rt712_sdca_probe(struct snd_soc_component *component) return 0; } +static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / 0x0a00; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * 0x0a00; + else { /* ADC gain */ + gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_priv *rt712) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) { + ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00; + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, + RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt712_sdca_set_fu1e_capture_ctl(rt712); + if (err < 0) + return err; + + return changed; +} + +static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt712_dmic_kctrl_priv *p = + (struct rt712_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt712_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt712_sdca_fu_info, \ + .get = rt712_sdca_dmic_fu1e_capture_get, \ + .put = rt712_sdca_dmic_fu1e_capture_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt712_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = { + RT712_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), + 1, 1, 4), + RT712_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv), + RT712_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, dmic_vol_tlv), +}; + +static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 0A Mux")) + mask_sft = 0; + else if (strstr(ucontrol->id.name, "ADC 0B Mux")) + mask_sft = 4; + else + return -EINVAL; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (((val >> mask_sft) & 0xf) == 0x4) ? 0 : 1; + + return 0; +} + +static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 0A Mux")) + mask_sft = 0; + else if (strstr(ucontrol->id.name, "ADC 0B Mux")) + mask_sft = 4; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val2); + val2 = ((0xf << mask_sft) & val2) >> mask_sft; + + if (val == 0) + val = 0x4; + else if (val >= 1) + val = 0xe; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, 0xf << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const adc_dmic_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt712_adc0a_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt712_adc0b_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc0a_mux = + SOC_DAPM_ENUM_EXT("ADC 0A Mux", rt712_adc0a_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc0b_mux = + SOC_DAPM_ENUM_EXT("ADC 0B Mux", rt712_adc0b_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static int rt712_sdca_dmic_fu1e_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt712->fu1e_dapm_mute = false; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + case SND_SOC_DAPM_PRE_PMD: + rt712->fu1e_dapm_mute = true; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + } + return 0; +} + +static int rt712_sdca_dmic_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_fu1e_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 0A Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc0a_mux), + SND_SOC_DAPM_MUX("ADC 0B Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc0b_mux), + + SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = { + {"DP8TX", NULL, "FU 1E"}, + + {"FU 1E", NULL, "PDE 11"}, + {"FU 1E", NULL, "ADC 0A Mux"}, + {"FU 1E", NULL, "ADC 0B Mux"}, + {"ADC 0A Mux", "DMIC1", "DMIC1"}, + {"ADC 0A Mux", "DMIC2", "DMIC2"}, + {"ADC 0B Mux", "DMIC1", "DMIC1"}, + {"ADC 0B Mux", "DMIC2", "DMIC2"}, +}; + +static int rt712_sdca_dmic_probe(struct snd_soc_component *component) +{ + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712->dmic_component = component; + + if (!rt712->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { .probe = rt712_sdca_probe, .controls = rt712_sdca_controls, @@ -995,6 +1382,20 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { .endianness = 1, }; +static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = { + .probe = rt712_sdca_dmic_probe, + .controls = rt712_sdca_dmic_snd_controls, + .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls), + .dapm_widgets = rt712_sdca_dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets), + .dapm_routes = rt712_sdca_dmic_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map), + .endianness = 1, +#ifdef CONFIG_DEBUG_FS + .debugfs_prefix = "dmic", +#endif +}; + static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) { @@ -1022,7 +1423,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, int retval, port, num_channels; unsigned int sampling_rate; - dev_dbg(dai->dev, "%s %s", __func__, dai->name); + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); sdw_stream = snd_soc_dai_get_dma_data(dai, substream); if (!sdw_stream) @@ -1031,6 +1432,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, if (!rt712->slave) return -EINVAL; + /* VA doesn't support AIF3 */ + if (dai->id == RT712_AIF3 && rt712->version_id == RT712_VA) + return -EINVAL; + /* SoundWire specific configuration */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { direction = SDW_DATA_DIR_RX; @@ -1044,6 +1449,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, direction = SDW_DATA_DIR_TX; if (dai->id == RT712_AIF1) port = 4; + else if (dai->id == RT712_AIF3) + port = 8; else return -EINVAL; } @@ -1105,6 +1512,14 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); break; + case RT712_AIF3: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; default: dev_err(component->dev, "%s: Wrong DAI id\n", __func__); return -EINVAL; @@ -1174,6 +1589,21 @@ static struct snd_soc_dai_driver rt712_sdca_dai[] = { } }; +static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = { + { + .name = "rt712-sdca-aif3", + .id = RT712_AIF3, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + } +}; + int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave) { @@ -1206,6 +1636,9 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, rt712->first_hw_init = false; rt712->fu0f_dapm_mute = true; rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true; + rt712->fu1e_dapm_mute = true; + rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] = + rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true; /* JD source uses JD1 in default */ rt712->jd_src = RT712_JD1; @@ -1239,35 +1672,11 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, return 0; } -int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) { - struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); int ret = 0; - unsigned int val, hibernation_flag; - - rt712->disable_irq = false; - - if (rt712->hw_init) - return 0; - - regcache_cache_only(rt712->regmap, false); - regcache_cache_only(rt712->mbq_regmap, false); - if (rt712->first_hw_init) { - regcache_cache_bypass(rt712->regmap, true); - regcache_cache_bypass(rt712->mbq_regmap, true); - } else { - /* - * PM runtime status is marked as 'active' only when a Slave reports as Attached - */ - - /* update count of parent 'active' children */ - pm_runtime_set_active(&slave->dev); - } - - pm_runtime_get_noresume(&slave->dev); - - rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); - rt712->hw_id = (val & 0xf000) >> 12; + unsigned int hibernation_flag; + struct device *dev = &rt712->slave->dev; rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); @@ -1307,6 +1716,131 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); } +} + +static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) +{ + int ret = 0; + unsigned int jack_func_status, mic_func_status, amp_func_status; + struct device *dev = &rt712->slave->dev; + + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status); + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &_func_status); + dev_dbg(dev, "%s jack/mic/amp func_status=0x%x, 0x%x, 0x%x\n", + __func__, jack_func_status, mic_func_status, amp_func_status); + + /* DMIC */ + if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_CH12_FLOAT_CTL, 0x0304); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL, 0x1f1e); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0B_FU_CH12_FLOAT_CTL, 0x0304); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_HDA_LEGACY_CONFIG_CTL0, 0x8010); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT11, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + rt712_sdca_index_write(rt712, RT712_ULTRA_SOUND_DET, RT712_ULTRA_SOUND_DETECTOR6, 0x3200); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + + /* Jack */ + if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_SEL_VEE2_HP_CTL1, 0x042a); + rt712_sdca_index_write(rt712, RT712_CHARGE_PUMP, RT712_HP_DET_CTL3, 0x1fff); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec67); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL1, 0x2802); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL6, 0xf215); + + /* calibration */ + ret = rt712_sdca_calibration(rt712); + if (ret < 0) + dev_err(dev, "%s, calibration failed!\n", __func__); + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, 0x3000, 0x0000); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_MISC_CTL_FOR_UAJ, 0x0003); + + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } + + /* SPK */ + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + if (rt712->hw_id != RT712_DEV_ID_713) { + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec63); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL1, 0xfff5); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); + } + /* clear flag */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), + FUNCTION_NEEDS_INITIALIZATION); + } +} + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val; + struct sdw_slave_prop *prop = &slave->prop; + + rt712->disable_irq = false; + + if (rt712->hw_init) + return 0; + + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, true); + regcache_cache_bypass(rt712->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); + rt712->hw_id = (val & 0xf000) >> 12; + rt712->version_id = (val & 0x0f00) >> 8; + dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id); + + if (rt712->version_id == RT712_VA) + rt712_sdca_va_io_init(rt712); + else { + /* multilanes and DMIC are supported by rt712vb */ + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712_dmic, rt712_sdca_dmic_dai, ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; + + prop->lane_control_support = true; + rt712_sdca_vb_io_init(rt712); + } /* * if set_jack callback occurred early than io_init, @@ -1315,8 +1849,7 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hs_jack) rt712_sdca_jack_init(rt712); - if (!hibernation_flag) - rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); if (rt712->first_hw_init) { regcache_cache_bypass(rt712->regmap, false); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index ff79e03118ce..2169f2f726b9 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -19,6 +19,7 @@ struct rt712_sdca_priv { struct regmap *regmap; struct regmap *mbq_regmap; struct snd_soc_component *component; + struct snd_soc_component *dmic_component; struct sdw_slave *slave; struct sdw_bus_params params; bool hw_init; @@ -34,13 +35,31 @@ struct rt712_sdca_priv { unsigned int scp_sdca_stat1; unsigned int scp_sdca_stat2; unsigned int hw_id; + unsigned int version_id; bool fu0f_dapm_mute; bool fu0f_mixer_l_mute; bool fu0f_mixer_r_mute; + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; }; +struct rt712_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* SDCA (Channel) */ +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 + /* NID */ #define RT712_VENDOR_REG 0x20 +#define RT712_EQ_CTRL 0x53 +#define RT712_CHARGE_PUMP 0x57 #define RT712_VENDOR_CALI 0x58 #define RT712_ULTRA_SOUND_DET 0x59 #define RT712_VENDOR_IMS_DRE 0x5b @@ -50,9 +69,13 @@ struct rt712_sdca_priv { /* Index (NID:20h) */ #define RT712_JD_PRODUCT_NUM 0x00 #define RT712_ANALOG_BIAS_CTL3 0x04 +#define RT712_JD_CTL1 0x09 +#define RT712_IO_CTL 0x0c #define RT712_LDO2_3_CTL1 0x0e #define RT712_PARA_VERB_CTL 0x1a #define RT712_CC_DET1 0x24 +#define RT712_CLASSD_AMP_CTL1 0x37 +#define RT712_CLASSD_AMP_CTL6 0x3c #define RT712_COMBO_JACK_AUTO_CTL1 0x45 #define RT712_COMBO_JACK_AUTO_CTL2 0x46 #define RT712_COMBO_JACK_AUTO_CTL3 0x47 @@ -61,6 +84,9 @@ struct rt712_sdca_priv { #define RT712_SW_CONFIG1 0x8a #define RT712_SW_CONFIG2 0x8b +/* Index (NID:57h) */ +#define RT712_HP_DET_CTL3 0x0c + /* Index (NID:58h) */ #define RT712_DAC_DC_CALI_CTL1 0x00 #define RT712_DAC_DC_CALI_CTL2 0x01 @@ -71,6 +97,7 @@ struct rt712_sdca_priv { /* Index (NID:5bh) */ #define RT712_IMS_DIGITAL_CTL1 0x00 #define RT712_IMS_DIGITAL_CTL5 0x05 +#define RT712_SEL_VEE2_HP_CTL1 0x23 #define RT712_HP_DETECT_RLDET_CTL1 0x29 #define RT712_HP_DETECT_RLDET_CTL2 0x2a @@ -109,6 +136,11 @@ struct rt712_sdca_priv { #define RT712_UMP_HID_CTL6 0x66 #define RT712_UMP_HID_CTL7 0x67 #define RT712_UMP_HID_CTL8 0x68 +#define RT712_MISC_CTL_FOR_UAJ 0x72 +#define RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL 0xa2 +#define RT712_DMIC2_FU_IT_FLOAT_CTL 0xa6 +#define RT712_ADC0B_FU_CH12_FLOAT_CTL 0xb0 +#define RT712_DMIC2_FU_CH12_FLOAT_CTL 0xb1 /* Parameter & Verb control 01 (0x1a)(NID:20h) */ #define RT712_HIDDEN_REG_SW_RESET (0x1 << 14) @@ -139,6 +171,7 @@ struct rt712_sdca_priv { #define FUNC_NUM_AMP 0x04 /* RT712 SDCA entity */ +#define RT712_SDCA_ENT0 0x00 #define RT712_SDCA_ENT_HID01 0x01 #define RT712_SDCA_ENT_GE49 0x49 #define RT712_SDCA_ENT_USER_FU05 0x05 @@ -157,6 +190,7 @@ struct rt712_sdca_priv { #define RT712_SDCA_ENT_CS1C 0x1c #define RT712_SDCA_ENT_CS31 0x31 #define RT712_SDCA_ENT_OT23 0x42 +#define RT712_SDCA_ENT_IT11 0x26 #define RT712_SDCA_ENT_IT26 0x26 #define RT712_SDCA_ENT_IT09 0x09 #define RT712_SDCA_ENT_PLATFORM_FU15 0x15 @@ -175,10 +209,12 @@ struct rt712_sdca_priv { #define RT712_SDCA_CTL_REQ_POWER_STATE 0x01 #define RT712_SDCA_CTL_VENDOR_DEF 0x30 #define RT712_SDCA_CTL_FU_CH_GAIN 0x0b +#define RT712_SDCA_CTL_FUNC_STATUS 0x10 -/* RT712 SDCA channel */ -#define CH_L 0x01 -#define CH_R 0x02 +/* Function_Status */ +#define FUNCTION_NEEDS_INITIALIZATION BIT(5) +#define FUNCTION_HAS_BEEN_RESET BIT(6) +#define FUNCTION_BUSY BIT(7) /* sample frequency index */ #define RT712_SDCA_RATE_16000HZ 0x04 @@ -191,6 +227,7 @@ struct rt712_sdca_priv { enum { RT712_AIF1, RT712_AIF2, + RT712_AIF3, }; enum rt712_sdca_jd_src { @@ -207,6 +244,11 @@ enum rt712_sdca_hw_id { #define RT712_PART_ID_713 0x713 +enum rt712_sdca_version { + RT712_VA, + RT712_VB, +}; + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave); diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index bf67de12d20b..240af0563283 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -9,12 +9,21 @@ #include <linux/regulator/consumer.h> #include <sound/soc.h> +#define MUX_TEXT_SIZE 2 +#define MUX_WIDGET_SIZE 4 +#define MUX_ROUTE_SIZE 3 struct simple_mux { struct gpio_desc *gpiod_mux; unsigned int mux; + const char *mux_texts[MUX_TEXT_SIZE]; + struct soc_enum mux_enum; + struct snd_kcontrol_new mux_mux; + struct snd_soc_dapm_widget mux_widgets[MUX_WIDGET_SIZE]; + struct snd_soc_dapm_route mux_routes[MUX_ROUTE_SIZE]; + struct snd_soc_component_driver mux_driver; }; -static const char * const simple_mux_texts[] = { +static const char * const simple_mux_texts[MUX_TEXT_SIZE] = { "Input 1", "Input 2" }; @@ -66,30 +75,23 @@ static unsigned int simple_mux_read(struct snd_soc_component *component, static const struct snd_kcontrol_new simple_mux_mux = SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put); -static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[] = { +static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[MUX_WIDGET_SIZE] = { SND_SOC_DAPM_INPUT("IN1"), SND_SOC_DAPM_INPUT("IN2"), - SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), + SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), // see simple_mux_probe() SND_SOC_DAPM_OUTPUT("OUT"), }; -static const struct snd_soc_dapm_route simple_mux_dapm_routes[] = { +static const struct snd_soc_dapm_route simple_mux_dapm_routes[MUX_ROUTE_SIZE] = { { "OUT", NULL, "MUX" }, - { "MUX", "Input 1", "IN1" }, - { "MUX", "Input 2", "IN2" }, -}; - -static const struct snd_soc_component_driver simple_mux_component_driver = { - .dapm_widgets = simple_mux_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(simple_mux_dapm_widgets), - .dapm_routes = simple_mux_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(simple_mux_dapm_routes), - .read = simple_mux_read, + { "MUX", "Input 1", "IN1" }, // see simple_mux_probe() + { "MUX", "Input 2", "IN2" }, // see simple_mux_probe() }; static int simple_mux_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct simple_mux *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -103,7 +105,30 @@ static int simple_mux_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), "Failed to get 'mux' gpio"); - return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); + /* Copy default settings */ + memcpy(&priv->mux_texts, &simple_mux_texts, sizeof(priv->mux_texts)); + memcpy(&priv->mux_enum, &simple_mux_enum, sizeof(priv->mux_enum)); + memcpy(&priv->mux_mux, &simple_mux_mux, sizeof(priv->mux_mux)); + memcpy(&priv->mux_widgets, &simple_mux_dapm_widgets, sizeof(priv->mux_widgets)); + memcpy(&priv->mux_routes, &simple_mux_dapm_routes, sizeof(priv->mux_routes)); + + priv->mux_driver.dapm_widgets = priv->mux_widgets; + priv->mux_driver.num_dapm_widgets = MUX_WIDGET_SIZE; + priv->mux_driver.dapm_routes = priv->mux_routes; + priv->mux_driver.num_dapm_routes = MUX_ROUTE_SIZE; + priv->mux_driver.read = simple_mux_read; + + /* Overwrite text ("Input 1", "Input 2") if property exists */ + of_property_read_string_array(np, "state-labels", priv->mux_texts, MUX_TEXT_SIZE); + + /* switch to use priv data instead of default */ + priv->mux_enum.texts = priv->mux_texts; + priv->mux_mux.private_value = (unsigned long)&priv->mux_enum; + priv->mux_widgets[2].kcontrol_news = &priv->mux_mux; + priv->mux_routes[1].control = priv->mux_texts[0]; // "Input 1" + priv->mux_routes[2].control = priv->mux_texts[1]; // "Input 2" + + return devm_snd_soc_register_component(dev, &priv->mux_driver, NULL, 0); } #ifdef CONFIG_OF diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index a7ed59ec49a6..9e68afc09897 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -13,7 +13,6 @@ #include <linux/device.h> #include <linux/i2c.h> #include <linux/gpio.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 1dc719d726ab..5eaddf07aadc 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -15,7 +15,6 @@ #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/soc.h> #include <sound/pcm.h> diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 67bc1c8b0131..5601fba17c96 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -20,7 +20,6 @@ #include <linux/firmware.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/soc.h> #include <sound/pcm.h> diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index a18ccf5fb7ad..6902bfef185b 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index 3aa81514dad7..1fbf4560f5cc 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // -// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// TAS2781 Common functions for HDA and ASoC Audio drivers // -// Copyright 2023 Texas Instruments, Inc. +// Copyright 2023 - 2024 Texas Instruments, Inc. // // Author: Shenghao Ding <shenghao-ding@ti.com> @@ -243,7 +243,7 @@ struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) } EXPORT_SYMBOL_GPL(tasdevice_kzalloc); -void tas2781_reset(struct tasdevice_priv *tas_dev) +void tasdevice_reset(struct tasdevice_priv *tas_dev) { int ret, i; @@ -254,8 +254,8 @@ void tas2781_reset(struct tasdevice_priv *tas_dev) } else { for (i = 0; i < tas_dev->ndev; i++) { ret = tasdevice_dev_write(tas_dev, i, - TAS2781_REG_SWRESET, - TAS2781_REG_SWRESET_RESET); + TASDEVICE_REG_SWRESET, + TASDEVICE_REG_SWRESET_RESET); if (ret < 0) dev_err(tas_dev->dev, "dev %d swreset fail, %d\n", @@ -264,7 +264,7 @@ void tas2781_reset(struct tasdevice_priv *tas_dev) } usleep_range(1000, 1050); } -EXPORT_SYMBOL_GPL(tas2781_reset); +EXPORT_SYMBOL_GPL(tasdevice_reset); int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, struct module *module, @@ -277,8 +277,13 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, */ mutex_lock(&tas_priv->codec_lock); - scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", - tas_priv->dev_name, tas_priv->ndev); + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", + tas_priv->name_prefix, tas_priv->dev_name, + tas_priv->ndev); + else + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); tas_priv->codec = codec; ret = request_firmware_nowait(module, FW_ACTION_UEVENT, diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 265a8ca25cbb..63626b982d04 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -21,7 +21,7 @@ #include <sound/soc.h> #include <sound/tlv.h> #include <sound/tas2781.h> - +#include <asm/unaligned.h> #define ERROR_PRAM_CRCCHK 0x0000000 #define ERROR_YRAM_CRCCHK 0x0000001 @@ -187,8 +187,7 @@ static struct tasdevice_config_info *tasdevice_add_config( /* convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - cfg_info->nblocks = - be32_to_cpup((__be32 *)&config_data[config_offset]); + cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; /* Several kinds of dsp/algorithm firmwares can run on tas2781, @@ -232,14 +231,14 @@ static struct tasdevice_config_info *tasdevice_add_config( } bk_da[i]->yram_checksum = - be16_to_cpup((__be16 *)&config_data[config_offset]); + get_unaligned_be16(&config_data[config_offset]); config_offset += 2; bk_da[i]->block_size = - be32_to_cpup((__be32 *)&config_data[config_offset]); + get_unaligned_be32(&config_data[config_offset]); config_offset += 4; bk_da[i]->n_subblks = - be32_to_cpup((__be32 *)&config_data[config_offset]); + get_unaligned_be32(&config_data[config_offset]); config_offset += 4; @@ -289,7 +288,7 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) } buf = (unsigned char *)fmw->data; - fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->img_sz = get_unaligned_be32(&buf[offset]); offset += 4; if (fw_hdr->img_sz != fmw->size) { dev_err(tas_priv->dev, @@ -300,9 +299,9 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) goto out; } - fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->checksum = get_unaligned_be32(&buf[offset]); offset += 4; - fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]); if (fw_hdr->binary_version_num < 0x103) { dev_err(tas_priv->dev, "File version 0x%04x is too low", fw_hdr->binary_version_num); @@ -311,7 +310,7 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) goto out; } offset += 4; - fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]); offset += 8; fw_hdr->plat_type = buf[offset]; offset += 1; @@ -339,11 +338,11 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) fw_hdr->devs[i] = buf[offset]; - fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->nconfig = get_unaligned_be32(&buf[offset]); offset += 4; for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { - fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]); offset += 4; total_config_sz += fw_hdr->config_size[i]; } @@ -423,7 +422,7 @@ static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, /* convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - block->type = be32_to_cpup((__be32 *)&data[offset]); + block->type = get_unaligned_be32(&data[offset]); offset += 4; block->is_pchksum_present = data[offset]; @@ -438,10 +437,10 @@ static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, block->ychksum = data[offset]; offset++; - block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + block->blk_size = get_unaligned_be32(&data[offset]); offset += 4; - block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + block->nr_subblocks = get_unaligned_be32(&data[offset]); offset += 4; /* fixed m68k compiling issue: @@ -482,7 +481,7 @@ static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, offset = -EINVAL; goto out; } - img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + img_data->nr_blk = get_unaligned_be32(&data[offset]); offset += 4; img_data->dev_blks = kcalloc(img_data->nr_blk, @@ -578,14 +577,14 @@ static int fw_parse_variable_header_kernel( offset = -EINVAL; goto out; } - fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + fw_hdr->device_family = get_unaligned_be16(&buf[offset]); if (fw_hdr->device_family != 0) { dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); offset = -EINVAL; goto out; } offset += 2; - fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + fw_hdr->device = get_unaligned_be16(&buf[offset]); if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || fw_hdr->device == 6) { dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); @@ -603,7 +602,7 @@ static int fw_parse_variable_header_kernel( goto out; } - tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]); offset += 4; if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > @@ -622,14 +621,14 @@ static int fw_parse_variable_header_kernel( for (i = 0; i < tas_fmw->nr_programs; i++) { program = &(tas_fmw->programs[i]); - program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + program->prog_size = get_unaligned_be32(&buf[offset]); offset += 4; } /* Skip the unused prog_size */ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); - tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]); offset += 4; /* The max number of config in firmware greater than 4 pieces of @@ -661,7 +660,7 @@ static int fw_parse_variable_header_kernel( for (i = 0; i < tas_fmw->nr_programs; i++) { config = &(tas_fmw->configs[i]); - config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + config->cfg_size = get_unaligned_be32(&buf[offset]); offset += 4; } @@ -699,7 +698,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, switch (subblk_typ) { case TASDEVICE_CMD_SING_W: { int i; - unsigned short len = be16_to_cpup((__be16 *)&data[2]); + unsigned short len = get_unaligned_be16(&data[2]); subblk_offset += 2; if (subblk_offset + 4 * len > sublocksize) { @@ -725,7 +724,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, } break; case TASDEVICE_CMD_BURST: { - unsigned short len = be16_to_cpup((__be16 *)&data[2]); + unsigned short len = get_unaligned_be16(&data[2]); subblk_offset += 2; if (subblk_offset + 4 + len > sublocksize) { @@ -766,7 +765,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, is_err = true; break; } - sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + sleep_time = get_unaligned_be16(&data[2]) * 1000; usleep_range(sleep_time, sleep_time + 50); subblk_offset += 2; } @@ -910,7 +909,7 @@ static int fw_parse_variable_hdr(struct tasdevice_priv offset += len; - fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->device_family = get_unaligned_be32(&buf[offset]); if (fw_hdr->device_family != 0) { dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); offset = -EINVAL; @@ -918,7 +917,7 @@ static int fw_parse_variable_hdr(struct tasdevice_priv } offset += 4; - fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + fw_hdr->device = get_unaligned_be32(&buf[offset]); if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || fw_hdr->device == 6) { dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); @@ -963,7 +962,7 @@ static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, offset = -EINVAL; goto out; } - block->type = be32_to_cpup((__be32 *)&data[offset]); + block->type = get_unaligned_be32(&data[offset]); offset += 4; if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { @@ -988,7 +987,7 @@ static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, block->is_ychksum_present = 0; } - block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + block->nr_cmds = get_unaligned_be32(&data[offset]); offset += 4; n = block->nr_cmds * 4; @@ -1039,7 +1038,7 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw, goto out; } offset += n; - img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + img_data->nr_blk = get_unaligned_be16(&data[offset]); offset += 2; img_data->dev_blks = kcalloc(img_data->nr_blk, @@ -1076,7 +1075,7 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv, offset = -EINVAL; goto out; } - tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]); offset += 2; if (tas_fmw->nr_programs == 0) { @@ -1143,7 +1142,7 @@ static int fw_parse_configuration_data( offset = -EINVAL; goto out; } - tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]); offset += 2; if (tas_fmw->nr_configurations == 0) { @@ -1775,7 +1774,7 @@ static int fw_parse_header(struct tasdevice_priv *tas_priv, /* Convert data[offset], data[offset + 1], data[offset + 2] and * data[offset + 3] into host */ - fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]); offset += 4; if (fw_fixed_hdr->fwsize != fmw->size) { dev_err(tas_priv->dev, "File size not match, %lu %u", @@ -1784,9 +1783,9 @@ static int fw_parse_header(struct tasdevice_priv *tas_priv, goto out; } offset += 4; - fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]); offset += 8; - fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]); offset += 72; out: @@ -1828,7 +1827,7 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, offset = -EINVAL; goto out; } - tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + tas_fmw->nr_calibrations = get_unaligned_be16(&data[offset]); offset += 2; if (tas_fmw->nr_calibrations != 1) { @@ -2324,14 +2323,21 @@ void tasdevice_tuning_switch(void *context, int state) struct tasdevice_fw *tas_fmw = tas_priv->fmw; int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + /* + * Only RCA-based Playback can still work with no dsp program running + * inside the chip. + */ + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + break; + default: return; } if (state == 0) { - if (tas_priv->cur_prog < tas_fmw->nr_programs) { - /*dsp mode or tuning mode*/ + if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { + /* dsp mode or tuning mode */ profile_cfg_id = tas_priv->rcabin.profile_cfg_id; tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog, tas_priv->cur_conf, @@ -2340,9 +2346,10 @@ void tasdevice_tuning_switch(void *context, int state) tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_POWER_UP); - } else + } else { tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_SHUTDOWN); + } } EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9350972dfefe..e79d613745b4 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/regmap.h> @@ -30,6 +31,7 @@ #include <sound/tas2781.h> #include <sound/tlv.h> #include <sound/tas2781-tlv.h> +#include <asm/unaligned.h> static const struct i2c_device_id tasdevice_id[] = { { "tas2563", TAS2563 }, @@ -103,7 +105,7 @@ static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, return tasdevice_amp_putvol(tas_priv, ucontrol, mc); } -static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -118,7 +120,7 @@ static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, return 0; } -static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, +static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = @@ -139,6 +141,106 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static int tas2563_digital_gain_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + unsigned int l = 0, r = mc->max; + unsigned int target, ar_mid, mid, ar_l, ar_r; + unsigned int reg = mc->reg; + unsigned char data[4]; + int ret; + + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + target = get_unaligned_be32(&data[0]); + + while (r > 1 + l) { + mid = (l + r) / 2; + ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]); + if (target < ar_mid) + r = mid; + else + l = mid; + } + + ar_l = get_unaligned_be32(tas2563_dvc_table[l]); + ar_r = get_unaligned_be32(tas2563_dvc_table[r]); + + /* find out the member same as or closer to the current volume */ + ucontrol->value.integer.value[0] = + abs(target - ar_l) <= abs(target - ar_r) ? l : r; +out: + mutex_unlock(&tas_dev->codec_lock); + return 0; +} + +static int tas2563_digital_gain_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int vol = ucontrol->value.integer.value[0]; + int status = 0, max = mc->max, rc = 1; + int i, ret; + unsigned int reg = mc->reg; + unsigned int volrd, volwr; + unsigned char data[4]; + + vol = clamp(vol, 0, max); + mutex_lock(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); + rc = -1; + goto out; + } + + volrd = get_unaligned_be32(&data[0]); + volwr = get_unaligned_be32(tas2563_dvc_table[vol]); + + if (volrd == volwr) { + rc = 0; + goto out; + } + + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_bulk_write(tas_dev, i, reg, + (unsigned char *)tas2563_dvc_table[vol], 4); + if (ret) { + dev_err(tas_dev->dev, + "%s, set digital vol error in dev %d\n", + __func__, i); + status |= BIT(i); + } + } + + if (status) + rc = -1; +out: + mutex_unlock(&tas_dev->codec_lock); + return rc; +} + +static const struct snd_kcontrol_new tasdevice_snd_controls[] = { + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tasdev_force_fwload_get, tasdev_force_fwload_put), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, @@ -146,8 +248,13 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, 0, 0, 200, 1, tas2781_digital_getvol, tas2781_digital_putvol, dvc_tlv), - SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, - tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static const struct snd_kcontrol_new tas2563_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0, + 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0, + tas2563_digital_gain_get, tas2563_digital_gain_put, + tas2563_dvc_tlv), }; static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, @@ -380,23 +487,41 @@ static void tasdevice_fw_ready(const struct firmware *fmw, mutex_lock(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) + if (ret) { + tasdevice_config_info_remove(tas_priv); goto out; + } tasdevice_create_control(tas_priv); tasdevice_dsp_remove(tas_priv); tasdevice_calbin_remove(tas_priv); - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", - tas_priv->dev_name); + /* + * The baseline is the RCA-only case, and then the code attempts to + * load DSP firmware but in case of failures just keep going, i.e. + * failing to load DSP firmware is NOT an error. + */ + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%s_coef.bin", + tas_priv->name_prefix, tas_priv->dev_name); + else + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; goto out; } - tasdevice_dsp_create_ctrls(tas_priv); + + /* + * If no dsp-related kcontrol created, the dsp resource will be freed. + */ + ret = tasdevice_dsp_create_ctrls(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dsp controls error\n"); + goto out; + } tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; @@ -404,8 +529,15 @@ static void tasdevice_fw_ready(const struct firmware *fmw, * calibrated data inside algo. */ for (i = 0; i < tas_priv->ndev; i++) { - scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", - tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + if (tas_priv->name_prefix) + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s-%s_cal_0x%02x.bin", tas_priv->name_prefix, + tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); + else + scnprintf(tas_priv->cal_binaryname[i], 64, + "%s_cal_0x%02x.bin", tas_priv->dev_name, + tas_priv->tasdevice[i].dev_addr); ret = tas2781_load_calibration(tas_priv, tas_priv->cal_binaryname[i], i); if (ret != 0) @@ -417,9 +549,8 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; out: - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - /*If DSP FW fail, kcontrol won't be created */ - tasdevice_config_info_remove(tas_priv); + if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { + /* If DSP FW fail, DSP kcontrol won't be created. */ tasdevice_dsp_remove(tas_priv); } mutex_unlock(&tas_priv->codec_lock); @@ -466,14 +597,14 @@ static int tasdevice_startup(struct snd_pcm_substream *substream, { struct snd_soc_component *codec = dai->component; struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int ret = 0; - if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); - ret = -EINVAL; + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + return 0; + default: + return -EINVAL; } - - return ret; } static int tasdevice_hw_params(struct snd_pcm_substream *substream, @@ -565,7 +696,28 @@ static struct snd_soc_dai_driver tasdevice_dai_driver[] = { static int tasdevice_codec_probe(struct snd_soc_component *codec) { struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct snd_kcontrol_new *p; + unsigned int size; + int rc; + + switch (tas_priv->chip_id) { + case TAS2781: + p = (struct snd_kcontrol_new *)tas2781_snd_controls; + size = ARRAY_SIZE(tas2781_snd_controls); + break; + default: + p = (struct snd_kcontrol_new *)tas2563_snd_controls; + size = ARRAY_SIZE(tas2563_snd_controls); + } + + rc = snd_soc_add_component_controls(codec, p, size); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: Add control err rc = %d", + __func__, rc); + return rc; + } + tas_priv->name_prefix = codec->name_prefix; return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready); } @@ -591,8 +743,8 @@ static const struct snd_soc_component_driver soc_codec_driver_tasdevice = { .probe = tasdevice_codec_probe, .remove = tasdevice_codec_remove, - .controls = tas2781_snd_controls, - .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .controls = tasdevice_snd_controls, + .num_controls = ARRAY_SIZE(tasdevice_snd_controls), .dapm_widgets = tasdevice_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), .dapm_routes = tasdevice_audio_map, @@ -622,33 +774,20 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) tas_priv->irq_info.irq_gpio = acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); - } else { + } else if (IS_ENABLED(CONFIG_OF)) { struct device_node *np = tas_priv->dev->of_node; -#ifdef CONFIG_OF - const __be32 *reg, *reg_end; - int len, sw, aw; - - aw = of_n_addr_cells(np); - sw = of_n_size_cells(np); - if (sw == 0) { - reg = (const __be32 *)of_get_property(np, - "reg", &len); - reg_end = reg + len/sizeof(*reg); - ndev = 0; - do { - dev_addrs[ndev] = of_read_number(reg, aw); - reg += aw; - ndev++; - } while (reg < reg_end); - } else { - ndev = 1; - dev_addrs[0] = client->addr; + u64 addr; + + for (i = 0; i < TASDEVICE_MAX_CHANNELS; i++) { + if (of_property_read_reg(np, i, &addr, NULL)) + break; + dev_addrs[ndev++] = addr; } -#else + + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } else { ndev = 1; dev_addrs[0] = client->addr; -#endif - tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); } tas_priv->ndev = ndev; for (i = 0; i < ndev; i++) @@ -714,6 +853,8 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) if (ret) goto err; + tasdevice_reset(tas_priv); + ret = devm_snd_soc_register_component(tas_priv->dev, &soc_codec_driver_tasdevice, tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); @@ -746,7 +887,7 @@ MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); static struct i2c_driver tasdevice_i2c_driver = { .driver = { - .name = "tas2781-codec", + .name = "tasdev-codec", .of_match_table = of_match_ptr(tasdevice_of_match), #ifdef CONFIG_ACPI .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 6d45df3b9ba4..4bc1fdd232bb 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -24,14 +24,13 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -246,7 +245,7 @@ struct tas5086_private { /* Current sample rate for de-emphasis control */ int rate; /* GPIO driving Reset pin, if any */ - int gpio_nreset; + struct gpio_desc *reset; struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; @@ -462,11 +461,11 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) static void tas5086_reset(struct tas5086_private *priv) { - if (gpio_is_valid(priv->gpio_nreset)) { + if (priv->reset) { /* Reset codec - minimum assertion time is 400ns */ - gpio_direction_output(priv->gpio_nreset, 0); + gpiod_direction_output(priv->reset, 1); udelay(1); - gpio_set_value(priv->gpio_nreset, 1); + gpiod_set_value(priv->reset, 0); /* Codec needs ~15ms to wake up */ msleep(15); @@ -867,9 +866,9 @@ static void tas5086_remove(struct snd_soc_component *component) { struct tas5086_private *priv = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(priv->gpio_nreset)) + if (priv->reset) /* Set codec to the reset state */ - gpio_set_value(priv->gpio_nreset, 0); + gpiod_set_value(priv->reset, 1); regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); }; @@ -914,7 +913,6 @@ static int tas5086_i2c_probe(struct i2c_client *i2c) { struct tas5086_private *priv; struct device *dev = &i2c->dev; - int gpio_nreset = -EINVAL; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -940,12 +938,11 @@ static int tas5086_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, priv); - gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_is_valid(gpio_nreset)) - if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) - gpio_nreset = -EINVAL; - - priv->gpio_nreset = gpio_nreset; + /* Request line asserted */ + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + gpiod_set_consumer_name(priv->reset, "TAS5086 Reset"); ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret < 0) { diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index e100cc9f5c19..7073b9d1cda8 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -25,7 +25,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/cdev.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -40,9 +39,10 @@ */ #define ADC3XXX_MICBIAS_PINS 2 +#define ADC3XXX_GPIO_PINS 2 /* Number of GPIO pins exposed via the gpiolib interface */ -#define ADC3XXX_GPIOS_MAX 2 +#define ADC3XXX_GPIOS_MAX (ADC3XXX_MICBIAS_PINS + ADC3XXX_GPIO_PINS) #define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 #define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -321,7 +321,8 @@ struct adc3xxx { struct gpio_desc *rst_pin; unsigned int pll_mode; unsigned int sysclk; - unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int gpio_cfg[ADC3XXX_GPIO_PINS]; /* value+1 (0 => not set) */ + unsigned int micbias_gpo[ADC3XXX_MICBIAS_PINS]; /* 1 => pin is GPO */ unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; int master; u8 page_no; @@ -329,7 +330,7 @@ struct adc3xxx { struct gpio_chip gpio_chip; }; -static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIO_PINS] = { ADC3XXX_GPIO1_CTRL, ADC3XXX_GPIO2_CTRL }; @@ -960,14 +961,23 @@ static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) if (offset >= ADC3XXX_GPIOS_MAX) return -EINVAL; - /* GPIO1 is offset 0, GPIO2 is offset 1 */ - /* We check here that the GPIO pins are either not configured in the - * DT, or that they purposely are set as outputs. - * (Input mode not yet implemented). - */ - if (adc3xxx->gpio_cfg[offset] != 0 && - adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) - return -EINVAL; + if (offset >= 0 && offset < ADC3XXX_GPIO_PINS) { + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured + * in the DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + } else if (offset >= ADC3XXX_GPIO_PINS && offset < ADC3XXX_GPIOS_MAX) { + /* MICBIAS1 is offset 2, MICBIAS2 is offset 3 */ + /* We check here if the MICBIAS pins are in fact configured + * as GPOs. + */ + if (!adc3xxx->micbias_gpo[offset - ADC3XXX_GPIO_PINS]) + return -EINVAL; + } return 0; } @@ -977,6 +987,21 @@ static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, { struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + /* For the MICBIAS pins, they are by definition outputs. */ + if (offset >= ADC3XXX_GPIO_PINS) { + unsigned int vg; + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + if (value) + vg = adc3xxx->micbias_vg[micbias]; + else + vg = ADC3XXX_MICBIAS_OFF; + return regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } + /* Set GPIO output function. */ return regmap_update_bits(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], @@ -1005,9 +1030,17 @@ static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) unsigned int regval; int ret; - /* We only allow output pins, so just read the value set in the output - * pin register field. - */ + /* We only allow output pins, so just read the value prevously set. */ + if (offset >= ADC3XXX_GPIO_PINS) { + /* MICBIAS pins */ + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + ret = regmap_read(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, ®val); + if (ret) + return ret; + return ((regval >> adc3xxx_micbias_shift[micbias]) & ADC3XXX_MICBIAS_MASK) != + ADC3XXX_MICBIAS_OFF; + } ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); if (ret) return ret; @@ -1049,7 +1082,7 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) * This allows us to set up things which are not software * controllable GPIOs, such as PDM microphone I/O, */ - for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + for (gpio = 0; gpio < ADC3XXX_GPIO_PINS; gpio++) { unsigned int cfg = adc3xxx->gpio_cfg[gpio]; if (cfg) { @@ -1061,9 +1094,15 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) } } - /* Set up micbias voltage */ + /* Set up micbias voltage. */ + /* If pin is configured as GPO, set off initially. */ for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { - unsigned int vg = adc3xxx->micbias_vg[micbias]; + unsigned int vg; + + if (adc3xxx->micbias_gpo[micbias]) + vg = ADC3XXX_MICBIAS_OFF; + else + vg = adc3xxx->micbias_vg[micbias]; regmap_update_bits(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, @@ -1091,8 +1130,19 @@ static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, return 0; } -static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, - const char *propname, unsigned int *vg) +static int adc3xxx_parse_dt_micbias_gpo(struct adc3xxx *adc3xxx, + const char *propname, + unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + + *cfg = of_property_read_bool(np, propname); + return 0; +} + +static int adc3xxx_parse_dt_micbias_vg(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) { struct device *dev = adc3xxx->dev; struct device_node *np = dev->of_node; @@ -1383,16 +1433,28 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c) dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); } + /* Configure mode for DMDIN/GPIO1 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); if (ret < 0) goto err_unprepare_mclk; + /* Configure mode for DMCLK/GPIO2 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + /* Configure mode for MICBIAS1: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias1-gpo", &adc3xxx->micbias_gpo[0]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure mode for MICBIAS2: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias2-gpo", &adc3xxx->micbias_gpo[1]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure voltage for MICBIAS1 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + /* Configure voltage for MICBIAS2 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); if (ret < 0) goto err_unprepare_mclk; diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 41342b340680..d594bf166c0e 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -12,7 +12,6 @@ #include <linux/regulator/consumer.h> #include <linux/acpi.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 4d7c5a80c6ed..2f94cfda0e33 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -23,7 +23,6 @@ #include <linux/regulator/consumer.h> #include <linux/acpi.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/jack.h> diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index dbf448dd8864..b9eb59e3bfa0 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -10,7 +10,6 @@ #include <linux/init.h> #include <linux/input.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/acpi.h> diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 0e6218ed0e5e..d589a212b768 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -50,7 +50,7 @@ struct wcd_mbhc { struct wcd_mbhc_config *cfg; const struct wcd_mbhc_cb *mbhc_cb; const struct wcd_mbhc_intr *intr_ids; - struct wcd_mbhc_field *fields; + const struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; /* Work to handle plug report */ @@ -1505,7 +1505,7 @@ EXPORT_SYMBOL(wcd_dt_parse_mbhc_data); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { struct device *dev = component->dev; diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index df68e99c81a3..b977e8f87d7c 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -279,7 +279,7 @@ 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, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en); int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr); @@ -300,7 +300,7 @@ static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { return ERR_PTR(-ENOTSUPP); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index deb15b95992d..373a31ddccb2 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -5,6 +5,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/bitops.h> @@ -297,7 +298,6 @@ struct wcd9335_codec { struct clk *mclk; struct clk *native_clk; u32 mclk_rate; - u8 version; struct slim_device *slim; struct slim_device *slim_ifc_dev; @@ -345,10 +345,6 @@ struct wcd9335_codec { int dmic_0_1_clk_cnt; int dmic_2_3_clk_cnt; int dmic_4_5_clk_cnt; - int dmic_sample_rate; - int mad_dmic_sample_rate; - - int native_clk_users; }; struct wcd9335_irq { @@ -397,13 +393,13 @@ struct interp_sample_rate { int rate_val; }; -static struct interp_sample_rate int_mix_rate_val[] = { +static const struct interp_sample_rate int_mix_rate_val[] = { {48000, 0x4}, /* 48K */ {96000, 0x5}, /* 96K */ {192000, 0x6}, /* 192K */ }; -static struct interp_sample_rate int_prim_rate_val[] = { +static const struct interp_sample_rate int_prim_rate_val[] = { {8000, 0x0}, /* 8K */ {16000, 0x1}, /* 16K */ {24000, -EINVAL},/* 24K */ @@ -1983,8 +1979,10 @@ static int wcd9335_trigger(struct snd_pcm_substream *substream, int cmd, } static int wcd9335_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct wcd9335_codec *wcd; int i; @@ -2012,7 +2010,7 @@ static int wcd9335_set_channel_map(struct snd_soc_dai *dai, return 0; } -static int wcd9335_get_channel_map(struct snd_soc_dai *dai, +static int wcd9335_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -2717,25 +2715,23 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); unsigned int decimator; char *dec_adc_mux_name = NULL; - char *widget_name = NULL; - char *wname; + char *widget_name; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; u16 tx_gain_ctl_reg; char *dec; u8 hpf_coff_freq; - widget_name = kmemdup_nul(w->name, 15, GFP_KERNEL); - if (!widget_name) + char *wname __free(kfree) = kmemdup_nul(w->name, 15, GFP_KERNEL); + if (!wname) return -ENOMEM; - wname = widget_name; + widget_name = wname; dec_adc_mux_name = strsep(&widget_name, " "); if (!dec_adc_mux_name) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, w->name); - ret = -EINVAL; - goto out; + return -EINVAL; } dec_adc_mux_name = widget_name; @@ -2743,16 +2739,14 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, if (!dec) { dev_err(comp->dev, "%s: decimator index not found\n", __func__); - ret = -EINVAL; - goto out; + return -EINVAL; } ret = kstrtouint(dec, 10, &decimator); if (ret < 0) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, wname); - ret = -EINVAL; - goto out; + return -EINVAL; } tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; @@ -2839,62 +2833,20 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); break; } -out: - kfree(wname); + return ret; } static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, - u32 mclk_rate, u32 dmic_clk_rate) + u32 mclk_rate) { - u32 div_factor; u8 dmic_ctl_val; - dev_err(component->dev, - "%s: mclk_rate = %d, dmic_sample_rate = %d\n", - __func__, mclk_rate, dmic_clk_rate); - - /* Default value to return in case of error */ if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; else dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - if (dmic_clk_rate == 0) { - dev_err(component->dev, - "%s: dmic_sample_rate cannot be 0\n", - __func__); - goto done; - } - - div_factor = mclk_rate / dmic_clk_rate; - switch (div_factor) { - case 2: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; - break; - case 3: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - break; - case 4: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; - break; - case 6: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; - break; - case 8: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; - break; - case 16: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; - break; - default: - dev_err(component->dev, - "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", - __func__, div_factor, mclk_rate, dmic_clk_rate); - break; - } - -done: return dmic_ctl_val; } @@ -2948,11 +2900,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->dmic_sample_rate); - + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)++; if (*dmic_clk_cnt == 1) { snd_soc_component_update_bits(comp, dmic_clk_reg, @@ -2964,10 +2912,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->mad_dmic_sample_rate); + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)--; if (*dmic_clk_cnt == 0) { snd_soc_component_update_bits(comp, dmic_clk_reg, @@ -4024,7 +3969,7 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) return ret; } -static struct wcd9335_irq wcd9335_irqs[] = { +static const struct wcd9335_irq wcd9335_irqs[] = { { .irq = WCD9335_IRQ_SLIMBUS, .handler = wcd9335_slimbus_irq, @@ -4961,7 +4906,7 @@ static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) } } -static struct regmap_config wcd9335_regmap_config = { +static const struct regmap_config wcd9335_regmap_config = { .reg_bits = 16, .val_bits = 8, .cache_type = REGCACHE_MAPLE, @@ -4985,7 +4930,7 @@ static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { }, }; -static struct regmap_config wcd9335_ifc_regmap_config = { +static const struct regmap_config wcd9335_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .can_multi_write = true, @@ -5032,22 +4977,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) int ret; wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (wcd->reset_gpio < 0) { - dev_err(dev, "Reset GPIO missing from DT\n"); - return wcd->reset_gpio; - } + if (wcd->reset_gpio < 0) + return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); wcd->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(wcd->mclk)) { - dev_err(dev, "mclk not found\n"); - return PTR_ERR(wcd->mclk); - } + if (IS_ERR(wcd->mclk)) + return dev_err_probe(dev, PTR_ERR(wcd->mclk), "mclk not found\n"); wcd->native_clk = devm_clk_get(dev, "slimbus"); - if (IS_ERR(wcd->native_clk)) { - dev_err(dev, "slimbus clock not found\n"); - return PTR_ERR(wcd->native_clk); - } + if (IS_ERR(wcd->native_clk)) + return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n"); wcd->supplies[0].supply = "vdd-buck"; wcd->supplies[1].supply = "vdd-buck-sido"; @@ -5056,10 +4995,8 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) wcd->supplies[4].supply = "vdd-io"; ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); - if (ret) { - dev_err(dev, "Failed to get supplies: err = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); return 0; } @@ -5107,7 +5044,6 @@ static int wcd9335_bring_up(struct wcd9335_codec *wcd) if (byte0 == 0x1) { dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n"); - wcd->version = WCD9335_VERSION_2_0; regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01); regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00); regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F); @@ -5159,10 +5095,8 @@ static int wcd9335_slim_probe(struct slim_device *slim) wcd->dev = dev; ret = wcd9335_parse_dt(wcd); - if (ret) { - dev_err(dev, "Error parsing DT: %d\n", ret); + if (ret) return ret; - } ret = wcd9335_power_on_reset(wcd); if (ret) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index de870c7819ca..291d0c80a6fc 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019, Linaro Limited +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/interrupt.h> @@ -475,17 +476,12 @@ enum { INTn_2_INP_SEL_PROXIMITY, }; -enum { - INTERP_MAIN_PATH, - INTERP_MIX_PATH, -}; - struct interp_sample_rate { int sample_rate; int rate_val; }; -static struct interp_sample_rate sr_val_tbl[] = { +static const struct interp_sample_rate sr_val_tbl[] = { {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, @@ -527,7 +523,7 @@ static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { }, }; -static struct regmap_config wcd934x_ifc_regmap_config = { +static const struct regmap_config wcd934x_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = 0xffff, @@ -571,10 +567,7 @@ struct wcd934x_codec { struct mutex micb_lock; u32 micb_ref[WCD934X_MAX_MICBIAS]; u32 pullup_ref[WCD934X_MAX_MICBIAS]; - u32 micb1_mv; u32 micb2_mv; - u32 micb3_mv; - u32 micb4_mv; }; #define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) @@ -1217,7 +1210,7 @@ static const struct soc_enum cdc_if_tx13_mux_enum = SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20), @@ -1923,8 +1916,10 @@ static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd, } static int wcd934x_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct wcd934x_codec *wcd; int i; @@ -1958,7 +1953,7 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai, return 0; } -static int wcd934x_get_channel_map(struct snd_soc_dai *dai, +static int wcd934x_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -2206,7 +2201,8 @@ static int wcd934x_get_micbias_val(struct device *dev, const char *micbias, mv = WCD934X_DEF_MICBIAS_MV; } - *micb_mv = mv; + if (micb_mv) + *micb_mv = mv; return (mv - 1000) / 50; } @@ -2218,17 +2214,14 @@ static int wcd934x_init_dmic(struct snd_soc_component *comp) u32 def_dmic_rate, dmic_clk_drv; vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias1-microvolt", - &wcd->micb1_mv); + "qcom,micbias1-microvolt", NULL); vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, "qcom,micbias2-microvolt", &wcd->micb2_mv); vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias3-microvolt", - &wcd->micb3_mv); + "qcom,micbias3-microvolt", NULL); vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias4-microvolt", - &wcd->micb4_mv); + "qcom,micbias4-microvolt", NULL); snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, WCD934X_MICB_VAL_MASK, vout_ctl_1); @@ -4981,25 +4974,23 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); unsigned int decimator; char *dec_adc_mux_name = NULL; - char *widget_name = NULL; - char *wname; + char *widget_name; int ret = 0, amic_n; u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; u16 tx_gain_ctl_reg; char *dec; u8 hpf_coff_freq; - widget_name = kstrndup(w->name, 15, GFP_KERNEL); - if (!widget_name) + char *wname __free(kfree) = kstrndup(w->name, 15, GFP_KERNEL); + if (!wname) return -ENOMEM; - wname = widget_name; + widget_name = wname; dec_adc_mux_name = strsep(&widget_name, " "); if (!dec_adc_mux_name) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, w->name); - ret = -EINVAL; - goto out; + return -EINVAL; } dec_adc_mux_name = widget_name; @@ -5007,16 +4998,14 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, if (!dec) { dev_err(comp->dev, "%s: decimator index not found\n", __func__); - ret = -EINVAL; - goto out; + return -EINVAL; } ret = kstrtouint(dec, 10, &decimator); if (ret < 0) { dev_err(comp->dev, "%s: Invalid decimator = %s\n", __func__, wname); - ret = -EINVAL; - goto out; + return -EINVAL; } tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; @@ -5109,8 +5098,7 @@ static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, WCD934X_DEC_PWR_LVL_DF); break; } -out: - kfree(wname); + return ret; } @@ -5864,17 +5852,13 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) struct device_node *ifc_dev_np; ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); - if (!ifc_dev_np) { - dev_err(dev, "No Interface device found\n"); - return -EINVAL; - } + if (!ifc_dev_np) + return dev_err_probe(dev, -EINVAL, "No Interface device found\n"); wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); of_node_put(ifc_dev_np); - if (!wcd->sidev) { - dev_err(dev, "Unable to get SLIM Interface device\n"); - return -EINVAL; - } + if (!wcd->sidev) + return dev_err_probe(dev, -EINVAL, "Unable to get SLIM Interface device\n"); slim_get_logical_addr(wcd->sidev); wcd->if_regmap = regmap_init_slimbus(wcd->sidev, @@ -5920,10 +5904,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) mutex_init(&wcd->micb_lock); ret = wcd934x_codec_parse_data(wcd); - if (ret) { - dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + if (ret) return ret; - } /* set default rate 9P6MHz */ regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c new file mode 100644 index 000000000000..3abc8041406a --- /dev/null +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + +#include <linux/component.h> +#include <linux/device.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc-dapm.h> +#include <sound/soc.h> +#include "wcd937x.h" + +static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_COMP_L, WCD937X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_COMP_R, WCD937X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_LO, WCD937X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DSD_L, WCD937X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), +}; + +static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC0, WCD937X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC1, WCD937X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_MBHC, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD937X_DMIC2, WCD937X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD937X_DMIC3, WCD937X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD937X_DMIC4, WCD937X_DMIC_4_6_PORT, BIT(0)), + WCD_SDW_CH(WCD937X_DMIC5, WCD937X_DMIC_4_6_PORT, BIT(1)), + WCD_SDW_CH(WCD937X_DMIC6, WCD937X_DMIC_4_6_PORT, BIT(2)), +}; + +static struct sdw_dpn_prop wcd937x_dpn_prop[WCD937X_MAX_SWR_PORTS] = { + { + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 8, + .simple_ch_prep_sm = true, + }, { + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 5, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd937x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); +} +EXPORT_SYMBOL_GPL(wcd937x_sdw_device_get); + +int wcd937x_sdw_hw_params(struct wcd937x_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[WCD937X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD937X_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); + wcd->sconfig.direction = wcd->is_tx ? SDW_DATA_DIR_TX : 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(wcd937x_sdw_hw_params); + +static int wcd9370_update_status(struct sdw_slave *slave, enum sdw_slave_status status) +{ + struct wcd937x_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; +} + +/* + * 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 wcd9370_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd937x_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, WCD937X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD937X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd937x_defaults[] = { + /* Default values except for Read-Only & Volatile registers */ + { WCD937X_ANA_BIAS, 0x00 }, + { WCD937X_ANA_RX_SUPPLIES, 0x00 }, + { WCD937X_ANA_HPH, 0x0c }, + { WCD937X_ANA_EAR, 0x00 }, + { WCD937X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD937X_ANA_TX_CH1, 0x20 }, + { WCD937X_ANA_TX_CH2, 0x00 }, + { WCD937X_ANA_TX_CH3, 0x20 }, + { WCD937X_ANA_TX_CH3_HPF, 0x00 }, + { WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD937X_ANA_MBHC_MECH, 0x39 }, + { WCD937X_ANA_MBHC_ELECT, 0x08 }, + { WCD937X_ANA_MBHC_ZDET, 0x00 }, + { WCD937X_ANA_MBHC_BTN0, 0x00 }, + { WCD937X_ANA_MBHC_BTN1, 0x10 }, + { WCD937X_ANA_MBHC_BTN2, 0x20 }, + { WCD937X_ANA_MBHC_BTN3, 0x30 }, + { WCD937X_ANA_MBHC_BTN4, 0x40 }, + { WCD937X_ANA_MBHC_BTN5, 0x50 }, + { WCD937X_ANA_MBHC_BTN6, 0x60 }, + { WCD937X_ANA_MBHC_BTN7, 0x70 }, + { WCD937X_ANA_MICB1, 0x10 }, + { WCD937X_ANA_MICB2, 0x10 }, + { WCD937X_ANA_MICB2_RAMP, 0x00 }, + { WCD937X_ANA_MICB3, 0x10 }, + { WCD937X_BIAS_CTL, 0x2a }, + { WCD937X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD937X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD937X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD937X_MBHC_CTL_CLK, 0x00 }, + { WCD937X_MBHC_CTL_ANA, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD937X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD937X_MBHC_CTL_BCS, 0x00 }, + { WCD937X_MBHC_TEST_CTL, 0x00 }, + { WCD937X_LDOH_MODE, 0x2b }, + { WCD937X_LDOH_BIAS, 0x68 }, + { WCD937X_LDOH_STB_LOADS, 0x00 }, + { WCD937X_LDOH_SLOWRAMP, 0x50 }, + { WCD937X_MICB1_TEST_CTL_1, 0x1a }, + { WCD937X_MICB1_TEST_CTL_2, 0x18 }, + { WCD937X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD937X_MICB2_TEST_CTL_1, 0x1a }, + { WCD937X_MICB2_TEST_CTL_2, 0x18 }, + { WCD937X_MICB2_TEST_CTL_3, 0xa4 }, + { WCD937X_MICB3_TEST_CTL_1, 0x1a }, + { WCD937X_MICB3_TEST_CTL_2, 0x18 }, + { WCD937X_MICB3_TEST_CTL_3, 0xa4 }, + { WCD937X_TX_COM_ADC_VCM, 0x39 }, + { WCD937X_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD937X_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD937X_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD937X_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD937X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD937X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD937X_TX_1_2_TEST_EN, 0xcc }, + { WCD937X_TX_1_2_ADC_IB, 0x09 }, + { WCD937X_TX_1_2_ATEST_REFCTL, 0x0a }, + { WCD937X_TX_1_2_TEST_CTL, 0x38 }, + { WCD937X_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD937X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_3_TEST_EN, 0xcc }, + { WCD937X_TX_3_ADC_IB, 0x09 }, + { WCD937X_TX_3_ATEST_REFCTL, 0x0a }, + { WCD937X_TX_3_TEST_CTL, 0x38 }, + { WCD937X_TX_3_TEST_BLK_EN, 0xff }, + { WCD937X_TX_3_TXFE_CLKDIV, 0x00 }, + { WCD937X_TX_3_SPARE_MONO, 0x00 }, + { WCD937X_CLASSH_MODE_1, 0x40 }, + { WCD937X_CLASSH_MODE_2, 0x3a }, + { WCD937X_CLASSH_MODE_3, 0x00 }, + { WCD937X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD937X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD937X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD937X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD937X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD937X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD937X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD937X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD937X_CLASSH_SPARE, 0x00 }, + { WCD937X_FLYBACK_EN, 0x4e }, + { WCD937X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD937X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD937X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD937X_FLYBACK_VNEG_CTRL_4, 0x7f }, + { WCD937X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD937X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD937X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD937X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD937X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 }, + { WCD937X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD937X_FLYBACK_CTRL_1, 0x65 }, + { WCD937X_FLYBACK_TEST_CTL, 0x00 }, + { WCD937X_RX_AUX_SW_CTL, 0x00 }, + { WCD937X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD937X_RX_TIMER_DIV, 0x32 }, + { WCD937X_RX_OCP_CTL, 0x1f }, + { WCD937X_RX_OCP_COUNT, 0x77 }, + { WCD937X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD937X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD937X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD937X_RX_BIAS_HPH_PA, 0xaa }, + { WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD937X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD937X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD937X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD937X_RX_BIAS_AUX_DAC, 0xa0 }, + { WCD937X_RX_BIAS_AUX_AMP, 0xaa }, + { WCD937X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD937X_RX_BIAS_MISC, 0x00 }, + { WCD937X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD937X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD937X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD937X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD937X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD937X_HPH_CNP_EN, 0x80 }, + { WCD937X_HPH_CNP_WG_CTL, 0x9a }, + { WCD937X_HPH_CNP_WG_TIME, 0x14 }, + { WCD937X_HPH_OCP_CTL, 0x28 }, + { WCD937X_HPH_AUTO_CHOP, 0x16 }, + { WCD937X_HPH_CHOP_CTL, 0x83 }, + { WCD937X_HPH_PA_CTL1, 0x46 }, + { WCD937X_HPH_PA_CTL2, 0x50 }, + { WCD937X_HPH_L_EN, 0x80 }, + { WCD937X_HPH_L_TEST, 0xe0 }, + { WCD937X_HPH_L_ATEST, 0x50 }, + { WCD937X_HPH_R_EN, 0x80 }, + { WCD937X_HPH_R_TEST, 0xe0 }, + { WCD937X_HPH_R_ATEST, 0x54 }, + { WCD937X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD937X_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD937X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD937X_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD937X_HPH_REFBUFF_LP_CTL, 0x0e }, + { WCD937X_HPH_L_DAC_CTL, 0x20 }, + { WCD937X_HPH_R_DAC_CTL, 0x20 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0x19 }, + { WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xa0 }, + { WCD937X_EAR_EAR_EN_REG, 0x22 }, + { WCD937X_EAR_EAR_PA_CON, 0x44 }, + { WCD937X_EAR_EAR_SP_CON, 0xdb }, + { WCD937X_EAR_EAR_DAC_CON, 0x80 }, + { WCD937X_EAR_EAR_CNP_FSM_CON, 0xb2 }, + { WCD937X_EAR_TEST_CTL, 0x00 }, + { WCD937X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD937X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD937X_SLEEP_CTL, 0x16 }, + { WCD937X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD937X_MBHC_NEW_CTL_1, 0x02 }, + { WCD937X_MBHC_NEW_CTL_2, 0x05 }, + { WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD937X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD937X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD937X_TX_NEW_TX_CH2_SEL, 0x00 }, + { WCD937X_AUX_AUXPA, 0x00 }, + { WCD937X_LDORXTX_MODE, 0x0c }, + { WCD937X_LDORXTX_CONFIG, 0x10 }, + { WCD937X_DIE_CRACK_DIE_CRK_DET_EN, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD937X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, + { WCD937X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD937X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER1, 0xfe }, + { WCD937X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD937X_HPH_NEW_INT_HPH_TIMER3, 0x4e }, + { WCD937X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD937X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD937X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xa8 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD937X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00 }, + { WCD937X_AUX_INT_EN_REG, 0x00 }, + { WCD937X_AUX_INT_PA_CTRL, 0x06 }, + { WCD937X_AUX_INT_SP_CTRL, 0xd2 }, + { WCD937X_AUX_INT_DAC_CTRL, 0x80 }, + { WCD937X_AUX_INT_CLK_CTRL, 0x50 }, + { WCD937X_AUX_INT_TEST_CTRL, 0x00 }, + { WCD937X_AUX_INT_STATUS_REG, 0x00 }, + { WCD937X_AUX_INT_MISC, 0x00 }, + { WCD937X_LDORXTX_INT_BIAS, 0x6e }, + { WCD937X_LDORXTX_INT_STB_LOADS_DTEST, 0x50 }, + { WCD937X_LDORXTX_INT_TEST0, 0x1c }, + { WCD937X_LDORXTX_INT_STARTUP_TIMER, 0xff }, + { WCD937X_LDORXTX_INT_TEST1, 0x1f }, + { WCD937X_LDORXTX_INT_STATUS, 0x00 }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a }, + { WCD937X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02 }, + { WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60 }, + { WCD937X_DIGITAL_PAGE_REGISTER, 0x00 }, + { WCD937X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD937X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x00 }, + { WCD937X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD937X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD937X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_RX0_CTL, 0xfc }, + { WCD937X_DIGITAL_CDC_RX1_CTL, 0xfc }, + { WCD937X_DIGITAL_CDC_RX2_CTL, 0xfc }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD937X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD937X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD937X_DIGITAL_CDC_RX_DELAY_CTL, 0x66 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R1, 0x4b }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R2, 0x26 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R3, 0x32 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R4, 0x57 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R5, 0x63 }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R6, 0x7c }, + { WCD937X_DIGITAL_CDC_HPH_DSM_R7, 0x57 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_0, 0xab }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1c }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A6_0, 0xaa }, + { WCD937X_DIGITAL_CDC_AUX_DSM_A7_0, 0xe3 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_0, 0x69 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_1, 0x54 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_2, 0x02 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_C_3, 0x15 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R1, 0xa4 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R2, 0xb5 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R3, 0x86 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R4, 0x85 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R5, 0xaa }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R6, 0xe2 }, + { WCD937X_DIGITAL_CDC_AUX_DSM_R7, 0x62 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xfc }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01 }, + { WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD937X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD937X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD937X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD937X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_AMIC_CTL, 0x07 }, + { WCD937X_DIGITAL_CDC_DMIC_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD937X_DIGITAL_CDC_DMIC3_CTL, 0x01 }, + { WCD937X_DIGITAL_EFUSE_CTL, 0x2b }, + { WCD937X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD937X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD937X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD937X_DIGITAL_INTR_MODE, 0x00 }, + { WCD937X_DIGITAL_INTR_MASK_0, 0xff }, + { WCD937X_DIGITAL_INTR_MASK_1, 0xff }, + { WCD937X_DIGITAL_INTR_MASK_2, 0x0f }, + { WCD937X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD937X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD937X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD937X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD937X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX0_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX1_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_RX2_CTL, 0x00 }, + { WCD937X_DIGITAL_CDC_CONN_TX_CTL, 0x00 }, + { WCD937X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD937X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD937X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD937X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 }, + { WCD937X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 }, + { WCD937X_DIGITAL_PAD_INP_DIS_0, 0x00 }, + { WCD937X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD937X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD937X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f }, + { WCD937X_DIGITAL_TX_DATA_EDGE_CTL, 0x10 }, + { WCD937X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD937X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD937X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD937X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD937X_DIGITAL_SSP_DBG, 0x00 }, + { WCD937X_DIGITAL_SPARE_0, 0x00 }, + { WCD937X_DIGITAL_SPARE_1, 0x00 }, + { WCD937X_DIGITAL_SPARE_2, 0x00 }, +}; + +static bool wcd937x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_BIAS: + case WCD937X_ANA_RX_SUPPLIES: + case WCD937X_ANA_HPH: + case WCD937X_ANA_EAR: + case WCD937X_ANA_EAR_COMPANDER_CTL: + case WCD937X_ANA_TX_CH1: + case WCD937X_ANA_TX_CH2: + case WCD937X_ANA_TX_CH3: + case WCD937X_ANA_TX_CH3_HPF: + case WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD937X_ANA_MICB3_DSP_EN_LOGIC: + case WCD937X_ANA_MBHC_MECH: + case WCD937X_ANA_MBHC_ELECT: + case WCD937X_ANA_MBHC_ZDET: + case WCD937X_ANA_MBHC_BTN0: + case WCD937X_ANA_MBHC_BTN1: + case WCD937X_ANA_MBHC_BTN2: + case WCD937X_ANA_MBHC_BTN3: + case WCD937X_ANA_MBHC_BTN4: + case WCD937X_ANA_MBHC_BTN5: + case WCD937X_ANA_MBHC_BTN6: + case WCD937X_ANA_MBHC_BTN7: + case WCD937X_ANA_MICB1: + case WCD937X_ANA_MICB2: + case WCD937X_ANA_MICB2_RAMP: + case WCD937X_ANA_MICB3: + case WCD937X_BIAS_CTL: + case WCD937X_BIAS_VBG_FINE_ADJ: + case WCD937X_LDOL_VDDCX_ADJUST: + case WCD937X_LDOL_DISABLE_LDOL: + case WCD937X_MBHC_CTL_CLK: + case WCD937X_MBHC_CTL_ANA: + case WCD937X_MBHC_CTL_SPARE_1: + case WCD937X_MBHC_CTL_SPARE_2: + case WCD937X_MBHC_CTL_BCS: + case WCD937X_MBHC_TEST_CTL: + case WCD937X_LDOH_MODE: + case WCD937X_LDOH_BIAS: + case WCD937X_LDOH_STB_LOADS: + case WCD937X_LDOH_SLOWRAMP: + case WCD937X_MICB1_TEST_CTL_1: + case WCD937X_MICB1_TEST_CTL_2: + case WCD937X_MICB1_TEST_CTL_3: + case WCD937X_MICB2_TEST_CTL_1: + case WCD937X_MICB2_TEST_CTL_2: + case WCD937X_MICB2_TEST_CTL_3: + case WCD937X_MICB3_TEST_CTL_1: + case WCD937X_MICB3_TEST_CTL_2: + case WCD937X_MICB3_TEST_CTL_3: + case WCD937X_TX_COM_ADC_VCM: + case WCD937X_TX_COM_BIAS_ATEST: + case WCD937X_TX_COM_ADC_INT1_IB: + case WCD937X_TX_COM_ADC_INT2_IB: + case WCD937X_TX_COM_TXFE_DIV_CTL: + case WCD937X_TX_COM_TXFE_DIV_START: + case WCD937X_TX_COM_TXFE_DIV_STOP_9P6M: + case WCD937X_TX_COM_TXFE_DIV_STOP_12P288M: + case WCD937X_TX_1_2_TEST_EN: + case WCD937X_TX_1_2_ADC_IB: + case WCD937X_TX_1_2_ATEST_REFCTL: + case WCD937X_TX_1_2_TEST_CTL: + case WCD937X_TX_1_2_TEST_BLK_EN: + case WCD937X_TX_1_2_TXFE_CLKDIV: + case WCD937X_TX_3_TEST_EN: + case WCD937X_TX_3_ADC_IB: + case WCD937X_TX_3_ATEST_REFCTL: + case WCD937X_TX_3_TEST_CTL: + case WCD937X_TX_3_TEST_BLK_EN: + case WCD937X_TX_3_TXFE_CLKDIV: + case WCD937X_CLASSH_MODE_1: + case WCD937X_CLASSH_MODE_2: + case WCD937X_CLASSH_MODE_3: + case WCD937X_CLASSH_CTRL_VCL_1: + case WCD937X_CLASSH_CTRL_VCL_2: + case WCD937X_CLASSH_CTRL_CCL_1: + case WCD937X_CLASSH_CTRL_CCL_2: + case WCD937X_CLASSH_CTRL_CCL_3: + case WCD937X_CLASSH_CTRL_CCL_4: + case WCD937X_CLASSH_CTRL_CCL_5: + case WCD937X_CLASSH_BUCK_TMUX_A_D: + case WCD937X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD937X_CLASSH_SPARE: + case WCD937X_FLYBACK_EN: + case WCD937X_FLYBACK_VNEG_CTRL_1: + case WCD937X_FLYBACK_VNEG_CTRL_2: + case WCD937X_FLYBACK_VNEG_CTRL_3: + case WCD937X_FLYBACK_VNEG_CTRL_4: + case WCD937X_FLYBACK_VNEG_CTRL_5: + case WCD937X_FLYBACK_VNEG_CTRL_6: + case WCD937X_FLYBACK_VNEG_CTRL_7: + case WCD937X_FLYBACK_VNEG_CTRL_8: + case WCD937X_FLYBACK_VNEG_CTRL_9: + case WCD937X_FLYBACK_VNEGDAC_CTRL_1: + case WCD937X_FLYBACK_VNEGDAC_CTRL_2: + case WCD937X_FLYBACK_VNEGDAC_CTRL_3: + case WCD937X_FLYBACK_CTRL_1: + case WCD937X_FLYBACK_TEST_CTL: + case WCD937X_RX_AUX_SW_CTL: + case WCD937X_RX_PA_AUX_IN_CONN: + case WCD937X_RX_TIMER_DIV: + case WCD937X_RX_OCP_CTL: + case WCD937X_RX_OCP_COUNT: + case WCD937X_RX_BIAS_EAR_DAC: + case WCD937X_RX_BIAS_EAR_AMP: + case WCD937X_RX_BIAS_HPH_LDO: + case WCD937X_RX_BIAS_HPH_PA: + case WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD937X_RX_BIAS_HPH_RDAC_LDO: + case WCD937X_RX_BIAS_HPH_CNP1: + case WCD937X_RX_BIAS_HPH_LOWPOWER: + case WCD937X_RX_BIAS_AUX_DAC: + case WCD937X_RX_BIAS_AUX_AMP: + case WCD937X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD937X_RX_BIAS_MISC: + case WCD937X_RX_BIAS_BUCK_RST: + case WCD937X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD937X_RX_BIAS_FLYB_ERRAMP: + case WCD937X_RX_BIAS_FLYB_BUFF: + case WCD937X_RX_BIAS_FLYB_MID_RST: + case WCD937X_HPH_CNP_EN: + case WCD937X_HPH_CNP_WG_CTL: + case WCD937X_HPH_CNP_WG_TIME: + case WCD937X_HPH_OCP_CTL: + case WCD937X_HPH_AUTO_CHOP: + case WCD937X_HPH_CHOP_CTL: + case WCD937X_HPH_PA_CTL1: + case WCD937X_HPH_PA_CTL2: + case WCD937X_HPH_L_EN: + case WCD937X_HPH_L_TEST: + case WCD937X_HPH_L_ATEST: + case WCD937X_HPH_R_EN: + case WCD937X_HPH_R_TEST: + case WCD937X_HPH_R_ATEST: + case WCD937X_HPH_RDAC_CLK_CTL1: + case WCD937X_HPH_RDAC_CLK_CTL2: + case WCD937X_HPH_RDAC_LDO_CTL: + case WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD937X_HPH_REFBUFF_UHQA_CTL: + case WCD937X_HPH_REFBUFF_LP_CTL: + case WCD937X_HPH_L_DAC_CTL: + case WCD937X_HPH_R_DAC_CTL: + case WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL: + case WCD937X_HPH_SURGE_HPHLR_SURGE_EN: + case WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1: + case WCD937X_EAR_EAR_EN_REG: + case WCD937X_EAR_EAR_PA_CON: + case WCD937X_EAR_EAR_SP_CON: + case WCD937X_EAR_EAR_DAC_CON: + case WCD937X_EAR_EAR_CNP_FSM_CON: + case WCD937X_EAR_TEST_CTL: + case WCD937X_HPH_NEW_ANA_HPH2: + case WCD937X_HPH_NEW_ANA_HPH3: + case WCD937X_SLEEP_CTL: + case WCD937X_SLEEP_WATCHDOG_CTL: + case WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD937X_MBHC_NEW_CTL_1: + case WCD937X_MBHC_NEW_CTL_2: + case WCD937X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD937X_MBHC_NEW_ZDET_ANA_CTL: + case WCD937X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD937X_TX_NEW_TX_CH2_SEL: + case WCD937X_AUX_AUXPA: + case WCD937X_LDORXTX_MODE: + case WCD937X_LDORXTX_CONFIG: + case WCD937X_DIE_CRACK_DIE_CRK_DET_EN: + case WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD937X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD937X_HPH_NEW_INT_PA_MISC1: + case WCD937X_HPH_NEW_INT_PA_MISC2: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD937X_HPH_NEW_INT_HPH_TIMER1: + case WCD937X_HPH_NEW_INT_HPH_TIMER2: + case WCD937X_HPH_NEW_INT_HPH_TIMER3: + case WCD937X_HPH_NEW_INT_HPH_TIMER4: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD937X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD937X_MBHC_NEW_INT_SPARE_2: + case WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON: + case WCD937X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD937X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS: + case WCD937X_AUX_INT_EN_REG: + case WCD937X_AUX_INT_PA_CTRL: + case WCD937X_AUX_INT_SP_CTRL: + case WCD937X_AUX_INT_DAC_CTRL: + case WCD937X_AUX_INT_CLK_CTRL: + case WCD937X_AUX_INT_TEST_CTRL: + case WCD937X_AUX_INT_MISC: + case WCD937X_LDORXTX_INT_BIAS: + case WCD937X_LDORXTX_INT_STB_LOADS_DTEST: + case WCD937X_LDORXTX_INT_TEST0: + case WCD937X_LDORXTX_INT_STARTUP_TIMER: + case WCD937X_LDORXTX_INT_TEST1: + case WCD937X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD937X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1: + case WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2: + case WCD937X_DIGITAL_CDC_RST_CTL: + case WCD937X_DIGITAL_TOP_CLK_CFG: + case WCD937X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD937X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD937X_DIGITAL_SWR_RST_EN: + case WCD937X_DIGITAL_CDC_PATH_MODE: + case WCD937X_DIGITAL_CDC_RX_RST: + case WCD937X_DIGITAL_CDC_RX0_CTL: + case WCD937X_DIGITAL_CDC_RX1_CTL: + case WCD937X_DIGITAL_CDC_RX2_CTL: + case WCD937X_DIGITAL_DEM_BYPASS_DATA0: + case WCD937X_DIGITAL_DEM_BYPASS_DATA1: + case WCD937X_DIGITAL_DEM_BYPASS_DATA2: + case WCD937X_DIGITAL_DEM_BYPASS_DATA3: + case WCD937X_DIGITAL_CDC_COMP_CTL_0: + case WCD937X_DIGITAL_CDC_RX_DELAY_CTL: + case WCD937X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD937X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD937X_DIGITAL_CDC_HPH_DSM_R1: + case WCD937X_DIGITAL_CDC_HPH_DSM_R2: + case WCD937X_DIGITAL_CDC_HPH_DSM_R3: + case WCD937X_DIGITAL_CDC_HPH_DSM_R4: + case WCD937X_DIGITAL_CDC_HPH_DSM_R5: + case WCD937X_DIGITAL_CDC_HPH_DSM_R6: + case WCD937X_DIGITAL_CDC_HPH_DSM_R7: + case WCD937X_DIGITAL_CDC_AUX_DSM_A1_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A1_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A2_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A2_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A3_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A3_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A4_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A4_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A5_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A5_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_A6_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_A7_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_0: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_1: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_2: + case WCD937X_DIGITAL_CDC_AUX_DSM_C_3: + case WCD937X_DIGITAL_CDC_AUX_DSM_R1: + case WCD937X_DIGITAL_CDC_AUX_DSM_R2: + case WCD937X_DIGITAL_CDC_AUX_DSM_R3: + case WCD937X_DIGITAL_CDC_AUX_DSM_R4: + case WCD937X_DIGITAL_CDC_AUX_DSM_R5: + case WCD937X_DIGITAL_CDC_AUX_DSM_R6: + case WCD937X_DIGITAL_CDC_AUX_DSM_R7: + case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1: + case WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2: + case WCD937X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD937X_DIGITAL_CDC_AUX_GAIN_CTL: + case WCD937X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD937X_DIGITAL_CDC_SWR_CLH: + case WCD937X_DIGITAL_SWR_CLH_BYP: + case WCD937X_DIGITAL_CDC_TX0_CTL: + case WCD937X_DIGITAL_CDC_TX1_CTL: + case WCD937X_DIGITAL_CDC_TX2_CTL: + case WCD937X_DIGITAL_CDC_TX_RST: + case WCD937X_DIGITAL_CDC_REQ_CTL: + case WCD937X_DIGITAL_CDC_AMIC_CTL: + case WCD937X_DIGITAL_CDC_DMIC_CTL: + case WCD937X_DIGITAL_CDC_DMIC1_CTL: + case WCD937X_DIGITAL_CDC_DMIC2_CTL: + case WCD937X_DIGITAL_CDC_DMIC3_CTL: + case WCD937X_DIGITAL_EFUSE_CTL: + case WCD937X_DIGITAL_EFUSE_PRG_CTL: + case WCD937X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD937X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD937X_DIGITAL_PDM_WD_CTL0: + case WCD937X_DIGITAL_PDM_WD_CTL1: + case WCD937X_DIGITAL_PDM_WD_CTL2: + case WCD937X_DIGITAL_INTR_MODE: + case WCD937X_DIGITAL_INTR_MASK_0: + case WCD937X_DIGITAL_INTR_MASK_1: + case WCD937X_DIGITAL_INTR_MASK_2: + case WCD937X_DIGITAL_INTR_CLEAR_0: + case WCD937X_DIGITAL_INTR_CLEAR_1: + case WCD937X_DIGITAL_INTR_CLEAR_2: + case WCD937X_DIGITAL_INTR_LEVEL_0: + case WCD937X_DIGITAL_INTR_LEVEL_1: + case WCD937X_DIGITAL_INTR_LEVEL_2: + case WCD937X_DIGITAL_INTR_SET_0: + case WCD937X_DIGITAL_INTR_SET_1: + case WCD937X_DIGITAL_INTR_SET_2: + case WCD937X_DIGITAL_INTR_TEST_0: + case WCD937X_DIGITAL_INTR_TEST_1: + case WCD937X_DIGITAL_INTR_TEST_2: + case WCD937X_DIGITAL_CDC_CONN_RX0_CTL: + case WCD937X_DIGITAL_CDC_CONN_RX1_CTL: + case WCD937X_DIGITAL_CDC_CONN_RX2_CTL: + case WCD937X_DIGITAL_CDC_CONN_TX_CTL: + case WCD937X_DIGITAL_LOOP_BACK_MODE: + case WCD937X_DIGITAL_SWR_DAC_TEST: + case WCD937X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD937X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD937X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD937X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD937X_DIGITAL_SWR_HM_TEST: + case WCD937X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD937X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD937X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD937X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD937X_DIGITAL_PAD_INP_DIS_0: + case WCD937X_DIGITAL_PAD_INP_DIS_1: + case WCD937X_DIGITAL_DRIVE_STRENGTH_0: + case WCD937X_DIGITAL_DRIVE_STRENGTH_1: + case WCD937X_DIGITAL_DRIVE_STRENGTH_2: + case WCD937X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD937X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD937X_DIGITAL_GPIO_MODE: + case WCD937X_DIGITAL_PIN_CTL_OE: + case WCD937X_DIGITAL_PIN_CTL_DATA_0: + case WCD937X_DIGITAL_PIN_CTL_DATA_1: + case WCD937X_DIGITAL_PIN_STATUS_0: + case WCD937X_DIGITAL_PIN_STATUS_1: + case WCD937X_DIGITAL_DIG_DEBUG_CTL: + case WCD937X_DIGITAL_DIG_DEBUG_EN: + case WCD937X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD937X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD937X_DIGITAL_SSP_DBG: + case WCD937X_DIGITAL_MODE_STATUS_0: + case WCD937X_DIGITAL_MODE_STATUS_1: + case WCD937X_DIGITAL_SPARE_0: + case WCD937X_DIGITAL_SPARE_1: + case WCD937X_DIGITAL_SPARE_2: + return true; + } + + return false; +} + +static bool wcd937x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_MBHC_RESULT_1: + case WCD937X_ANA_MBHC_RESULT_2: + case WCD937X_ANA_MBHC_RESULT_3: + case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD937X_TX_1_2_SAR2_ERR: + case WCD937X_TX_1_2_SAR1_ERR: + case WCD937X_TX_3_SPARE_MONO: + case WCD937X_TX_3_SAR1_ERR: + case WCD937X_HPH_L_STATUS: + case WCD937X_HPH_R_STATUS: + case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS: + case WCD937X_EAR_STATUS_REG_1: + case WCD937X_EAR_STATUS_REG_2: + case WCD937X_MBHC_NEW_FSM_STATUS: + case WCD937X_MBHC_NEW_ADC_RESULT: + case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT: + case WCD937X_AUX_INT_STATUS_REG: + case WCD937X_LDORXTX_INT_STATUS: + case WCD937X_DIGITAL_CHIP_ID0: + case WCD937X_DIGITAL_CHIP_ID1: + case WCD937X_DIGITAL_CHIP_ID2: + case WCD937X_DIGITAL_CHIP_ID3: + case WCD937X_DIGITAL_EFUSE_T_DATA_0: + case WCD937X_DIGITAL_EFUSE_T_DATA_1: + case WCD937X_DIGITAL_INTR_STATUS_0: + case WCD937X_DIGITAL_INTR_STATUS_1: + case WCD937X_DIGITAL_INTR_STATUS_2: + case WCD937X_DIGITAL_EFUSE_REG_0: + case WCD937X_DIGITAL_EFUSE_REG_1: + case WCD937X_DIGITAL_EFUSE_REG_2: + case WCD937X_DIGITAL_EFUSE_REG_3: + case WCD937X_DIGITAL_EFUSE_REG_4: + case WCD937X_DIGITAL_EFUSE_REG_5: + case WCD937X_DIGITAL_EFUSE_REG_6: + case WCD937X_DIGITAL_EFUSE_REG_7: + case WCD937X_DIGITAL_EFUSE_REG_8: + case WCD937X_DIGITAL_EFUSE_REG_9: + case WCD937X_DIGITAL_EFUSE_REG_10: + case WCD937X_DIGITAL_EFUSE_REG_11: + case WCD937X_DIGITAL_EFUSE_REG_12: + case WCD937X_DIGITAL_EFUSE_REG_13: + case WCD937X_DIGITAL_EFUSE_REG_14: + case WCD937X_DIGITAL_EFUSE_REG_15: + case WCD937X_DIGITAL_EFUSE_REG_16: + case WCD937X_DIGITAL_EFUSE_REG_17: + case WCD937X_DIGITAL_EFUSE_REG_18: + case WCD937X_DIGITAL_EFUSE_REG_19: + case WCD937X_DIGITAL_EFUSE_REG_20: + case WCD937X_DIGITAL_EFUSE_REG_21: + case WCD937X_DIGITAL_EFUSE_REG_22: + case WCD937X_DIGITAL_EFUSE_REG_23: + case WCD937X_DIGITAL_EFUSE_REG_24: + case WCD937X_DIGITAL_EFUSE_REG_25: + case WCD937X_DIGITAL_EFUSE_REG_26: + case WCD937X_DIGITAL_EFUSE_REG_27: + case WCD937X_DIGITAL_EFUSE_REG_28: + case WCD937X_DIGITAL_EFUSE_REG_29: + case WCD937X_DIGITAL_EFUSE_REG_30: + case WCD937X_DIGITAL_EFUSE_REG_31: + return true; + } + + return wcd937x_rdwr_register(dev, reg); +} + +static bool wcd937x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD937X_ANA_MBHC_RESULT_1: + case WCD937X_ANA_MBHC_RESULT_2: + case WCD937X_ANA_MBHC_RESULT_3: + case WCD937X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD937X_TX_1_2_SAR1_ERR: + case WCD937X_TX_1_2_SAR2_ERR: + case WCD937X_TX_3_SAR1_ERR: + case WCD937X_HPH_L_STATUS: + case WCD937X_HPH_R_STATUS: + case WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS: + case WCD937X_EAR_STATUS_REG_1: + case WCD937X_EAR_STATUS_REG_2: + case WCD937X_MBHC_NEW_FSM_STATUS: + case WCD937X_MBHC_NEW_ADC_RESULT: + case WCD937X_DIE_CRACK_DIE_CRK_DET_OUT: + case WCD937X_DIGITAL_INTR_STATUS_0: + case WCD937X_DIGITAL_INTR_STATUS_1: + case WCD937X_DIGITAL_INTR_STATUS_2: + case WCD937X_DIGITAL_SWR_HM_TEST: + case WCD937X_DIGITAL_PIN_STATUS_0: + case WCD937X_DIGITAL_PIN_STATUS_1: + case WCD937X_DIGITAL_MODE_STATUS_0: + case WCD937X_DIGITAL_MODE_STATUS_1: + return true; + } + return false; +} + +static const struct regmap_config wcd937x_regmap_config = { + .name = "wcd937x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd937x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd937x_defaults), + .max_register = WCD937X_MAX_REGISTER, + .readable_reg = wcd937x_readable_register, + .writeable_reg = wcd937x_rdwr_register, + .volatile_reg = wcd937x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9370_slave_ops = { + .update_status = wcd9370_update_status, + .interrupt_callback = wcd9370_interrupt_callback, +}; + +static int wcd937x_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 wcd937x_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 wcd937x_sdw_component_ops = { + .bind = wcd937x_sdw_component_bind, + .unbind = wcd937x_sdw_component_unbind, +}; + +static int wcd9370_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd937x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /* Port map index starts at 0, however the data port for this codec start at 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], + WCD937X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD937X_MAX_SWR_PORTS); + } + if (ret < 0) + dev_info(dev, "Error getting static port mapping for %s (%d)\n", + wcd->is_tx ? "TX" : "RX", ret); + + 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(WCD937X_MAX_TX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd937x_dpn_prop; + wcd->ch_info = &wcd937x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + + wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_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); + } else { + pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd937x_dpn_prop; + wcd->ch_info = &wcd937x_sdw_rx_ch_info[0]; + } + + + ret = component_add(dev, &wcd937x_sdw_component_ops); + if (ret) + return ret; + + /* Set suspended until aggregate device is bind */ + pm_runtime_set_suspended(dev); + + return 0; +} + +static int wcd9370_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + + component_del(dev, &wcd937x_sdw_component_ops); + + return 0; +} + +static const struct sdw_device_id wcd9370_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10a, 0), /* WCD9370 RX/TX Device ID */ + { }, +}; +MODULE_DEVICE_TABLE(sdw, wcd9370_slave_id); + +static int __maybe_unused wcd937x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd937x_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 wcd937x_sdw_runtime_resume(struct device *dev) +{ + struct wcd937x_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 wcd937x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd937x_sdw_runtime_suspend, wcd937x_sdw_runtime_resume, NULL) +}; + +static struct sdw_driver wcd9370_codec_driver = { + .probe = wcd9370_probe, + .remove = wcd9370_remove, + .ops = &wcd9370_slave_ops, + .id_table = wcd9370_slave_id, + .driver = { + .name = "wcd9370-codec", + .pm = &wcd937x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9370_codec_driver); + +MODULE_DESCRIPTION("WCD937X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c new file mode 100644 index 000000000000..13926f4b0d9f --- /dev/null +++ b/sound/soc/codecs/wcd937x.c @@ -0,0 +1,2971 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/pcm.h> +#include <sound/soc-dapm.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#include "wcd937x.h" + +enum { + CHIPID_WCD9370 = 0, + CHIPID_WCD9375 = 5, +}; + +/* Z value defined in milliohm */ +#define WCD937X_ZDET_VAL_32 (32000) +#define WCD937X_ZDET_VAL_400 (400000) +#define WCD937X_ZDET_VAL_1200 (1200000) +#define WCD937X_ZDET_VAL_100K (100000000) +/* Z floating defined in ohms */ +#define WCD937X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD937X_ZDET_NUM_MEASUREMENTS (900) +#define WCD937X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14) +#define WCD937X_MBHC_GET_X1(x) ((x) & 0x3FFF) +/* Z value compared in milliOhm */ +#define WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z) (((z) > 400000) || ((z) < 32000)) +#define WCD937X_MBHC_ZDET_CONST (86 * 16384) +#define WCD937X_MBHC_MOISTURE_RREF R_24_KOHM +#define WCD_MBHC_HS_V_MAX 1600 +#define EAR_RX_PATH_AUX 1 +#define WCD937X_MBHC_MAX_BUTTONS 8 + +#define WCD937X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) + +/* Fractional Rates */ +#define WCD937X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WCD937X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +enum { + ALLOW_BUCK_DISABLE, + HPH_COMP_DELAY, + HPH_PA_DELAY, + AMIC2_BCS_ENABLE, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +struct wcd937x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd937x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode; + struct device_node *txnode; + struct regmap *regmap; + /* micb setup lock */ + struct mutex micb_lock; + /* 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[WCD937X_MAX_BULK_SUPPLY]; + struct regulator *buck_supply; + struct snd_soc_jack *jack; + unsigned long status_mask; + s32 micb_ref[WCD937X_MAX_MICBIAS]; + s32 pullup_ref[WCD937X_MAX_MICBIAS]; + u32 hph_mode; + int ear_rx_path; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int aux_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + + struct gpio_desc *us_euro_gpio; + struct gpio_desc *reset_gpio; + + atomic_t rx_clk_cnt; + atomic_t ana_clk_count; +}; + +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); + +struct wcd937x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD937X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD937X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD937X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD937X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD937X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD937X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD937X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD937X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD937X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD937X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD937X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD937X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD937X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD937X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD937X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD937X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD937X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD937X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD937X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD937X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD937X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD937X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD937X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD937X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD937X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD937X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD937X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD937X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD937X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD937X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD937X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD937X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD937X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD937X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD937X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD937X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD937X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD937X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd937x_irqs[WCD937X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_SW_DET, 0, BIT(4)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_OCP_INT, 0, BIT(5)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_CNP_INT, 0, BIT(6)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_OCP_INT, 0, BIT(7)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_CNP_INT, 1, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_CNP_INT, 1, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_EAR_SCD_INT, 1, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_CNP_INT, 1, BIT(3)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_SCD_INT, 1, BIT(4)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)), + REGMAP_IRQ_REG(WCD937X_IRQ_AUX_PDM_WD_INT, 1, BIT(7)), + REGMAP_IRQ_REG(WCD937X_IRQ_LDORT_SCD_INT, 2, BIT(0)), + REGMAP_IRQ_REG(WCD937X_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)), + REGMAP_IRQ_REG(WCD937X_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)), +}; + +static int wcd937x_handle_post_irq(void *data) +{ + struct wcd937x_priv *wcd937x; + + if (data) + wcd937x = (struct wcd937x_priv *)data; + else + return IRQ_HANDLED; + + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_0, 0); + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_1, 0); + regmap_write(wcd937x->regmap, WCD937X_DIGITAL_INTR_CLEAR_2, 0); + + return IRQ_HANDLED; +} + +static const u32 wcd937x_config_regs[] = { + WCD937X_DIGITAL_INTR_LEVEL_0, +}; + +static const struct regmap_irq_chip wcd937x_regmap_irq_chip = { + .name = "wcd937x", + .irqs = wcd937x_irqs, + .num_irqs = ARRAY_SIZE(wcd937x_irqs), + .num_regs = 3, + .status_base = WCD937X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD937X_DIGITAL_INTR_MASK_0, + .ack_base = WCD937X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .clear_ack = 1, + .config_base = wcd937x_config_regs, + .num_config_bases = ARRAY_SIZE(wcd937x_config_regs), + .num_config_regs = 1, + .runtime_pm = true, + .handle_post_irq = wcd937x_handle_post_irq, + .irq_drv_data = NULL, +}; + +static void wcd937x_reset(struct wcd937x_priv *wcd937x) +{ + usleep_range(20, 30); + + gpiod_set_value(wcd937x->reset_gpio, 1); + + usleep_range(20, 30); +} + +static void wcd937x_io_init(struct regmap *regmap) +{ + u32 val = 0, temp = 0, temp1 = 0; + + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_29, &val); + + val = val & 0x0F; + + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &temp); + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_17, &temp1); + + if (temp == 0x02 || temp1 > 0x09) + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0E, val); + else + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x0e, 0x0e); + + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x80, 0x80); + usleep_range(1000, 1010); + + regmap_update_bits(regmap, WCD937X_SLEEP_CTL, 0x40, 0x40); + usleep_range(1000, 1010); + + regmap_update_bits(regmap, WCD937X_LDORXTX_CONFIG, BIT(4), 0x00); + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xf0, BIT(7)); + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(7), BIT(7)); + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), BIT(6)); + usleep_range(10000, 10010); + + regmap_update_bits(regmap, WCD937X_ANA_BIAS, BIT(6), 0x00); + regmap_update_bits(regmap, WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xff, 0xd9); + regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_1, 0xff, 0xfa); + regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_1, 0xff, 0xfa); + regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_1, 0xff, 0xfa); + + regmap_update_bits(regmap, WCD937X_MICB1_TEST_CTL_2, 0x38, 0x00); + regmap_update_bits(regmap, WCD937X_MICB2_TEST_CTL_2, 0x38, 0x00); + regmap_update_bits(regmap, WCD937X_MICB3_TEST_CTL_2, 0x38, 0x00); + + /* Set Bandgap Fine Adjustment to +5mV for Tanggu SMIC part */ + regmap_read(regmap, WCD937X_DIGITAL_EFUSE_REG_16, &val); + if (val == 0x01) { + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0); + } else if (val == 0x02) { + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x1F, 0x04); + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x1F, 0x04); + regmap_update_bits(regmap, WCD937X_BIAS_VBG_FINE_ADJ, 0xF0, 0xB0); + regmap_update_bits(regmap, WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xF0, 0x50); + } +} + +static int wcd937x_rx_clk_enable(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (atomic_read(&wcd937x->rx_clk_cnt)) + return 0; + + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), BIT(0)); + snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), BIT(0)); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX0_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX1_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_RX2_CTL, BIT(6), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), BIT(1)); + + atomic_inc(&wcd937x->rx_clk_cnt); + + return 0; +} + +static int wcd937x_rx_clk_disable(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (!atomic_read(&wcd937x->rx_clk_cnt)) { + dev_err(component->dev, "clk already disabled\n"); + return 0; + } + + atomic_dec(&wcd937x->rx_clk_cnt); + + snd_soc_component_update_bits(component, WCD937X_ANA_RX_SUPPLIES, BIT(0), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(1), 0x00); + snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(0), 0x00); + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_HPH_RDAC_CLK_CTL1, + BIT(7), 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, 0x06); + + if (wcd937x->comp1_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), 0x00); + + if (wcd937x->comp2_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, BIT(5), 0x00); + } + + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5110); + clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + } + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), 0x00); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), BIT(5)); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(0)); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + WCD937X_HPH_RDAC_CLK_CTL1, BIT(7), 0x00); + set_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, 0x06); + if (wcd937x->comp2_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, BIT(5), 0x00); + if (wcd937x->comp1_enable) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_HPH_L_EN, + BIT(5), 0x00); + } + + if (test_bit(HPH_COMP_DELAY, &wcd937x->status_mask)) { + usleep_range(5000, 5110); + clear_bit(HPH_COMP_DELAY, &wcd937x->status_mask); + } + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(0), 0x00); + snd_soc_component_update_bits(component, + WCD937X_HPH_R_EN, + BIT(5), BIT(5)); + } + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R, + 0x0f, BIT(0)); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_HPH_GAIN_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), BIT(0)); + + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(1)); + else if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, 0x06); + if (wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), BIT(1)); + usleep_range(5000, 5010); + + snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN, BIT(2), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + if (hph_mode == CLS_AB_HIFI || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_HIFI) + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L, + 0x0f, BIT(0)); + if (wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_COMP_CTL_0, + BIT(1), 0x00); + break; + } + + return 0; +} + +static int wcd937x_codec_aux_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd937x_rx_clk_enable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(2), BIT(2)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_AUX_GAIN_CTL, + BIT(0), BIT(0)); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_AUX, + hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(2), 0x00); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(4), BIT(4)); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL1, + 0x07, 0x03); + break; + case SND_SOC_DAPM_POST_PMU: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (wcd937x->comp2_enable) + usleep_range(7000, 7100); + else + usleep_range(20000, 20100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), BIT(1)); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->hphr_pdm_wd_int); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHR_PA_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (wcd937x->comp2_enable) + usleep_range(7000, 7100); + else + usleep_range(20000, 20100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHR_PA_OFF); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL1, 0x07, 0x00); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(4), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_ANA_HPH, + BIT(5), BIT(5)); + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x03); + break; + case SND_SOC_DAPM_POST_PMU: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + snd_soc_component_update_bits(component, + WCD937X_HPH_NEW_INT_HPH_TIMER1, + BIT(1), BIT(1)); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + set_bit(HPH_PA_DELAY, &wcd937x->status_mask); + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(HPH_PA_DELAY, &wcd937x->status_mask)) { + if (!wcd937x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd937x->status_mask); + } + + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, 0x07, 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_HPH, BIT(5), 0x00); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_aux_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), BIT(0)); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + enable_irq(wcd937x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(2000, 2010); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_AUX, + hph_mode); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), 0x00); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd937x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable watchdog interrupt for HPHL or AUX depending on mux value */ + wcd937x->ear_rx_path = snd_soc_component_read(component, + WCD937X_DIGITAL_CDC_EAR_PATH_CTL); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), BIT(0)); + else + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, + 0x07, 0x03); + if (!wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_ANA_EAR_COMPANDER_CTL, + BIT(7), BIT(7)); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(6000, 6010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI) + snd_soc_component_update_bits(component, + WCD937X_ANA_RX_SUPPLIES, + BIT(1), BIT(1)); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + enable_irq(wcd937x->aux_pdm_wd_int); + else + enable_irq(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + else + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + if (!wcd937x->comp1_enable) + snd_soc_component_update_bits(component, + WCD937X_ANA_EAR_COMPANDER_CTL, + BIT(7), 0x00); + usleep_range(7000, 7010); + wcd_clsh_ctrl_set_state(wcd937x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + hph_mode); + snd_soc_component_update_bits(component, WCD937X_FLYBACK_EN, + BIT(2), BIT(2)); + + if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL2, + BIT(0), 0x00); + else + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_PDM_WD_CTL0, + 0x07, 0x00); + break; + } + + return 0; +} + +static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(0), 0x00); + } + + return 0; +} + +static int wcd937x_enable_rx2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(1), 0x00); + } + + return 0; +} + +static int wcd937x_enable_rx3(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (event == SND_SOC_DAPM_POST_PMD) { + usleep_range(6000, 6010); + wcd937x_rx_clk_disable(component); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(2), 0x00); + } + + return 0; +} + +static int wcd937x_get_micb_vout_ctl_val(u32 micb_mv) +{ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("Unsupported micbias voltage (%u mV)\n", micb_mv); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + bool use_amic3 = snd_soc_component_read(component, WCD937X_TX_NEW_TX_CH2_SEL) & BIT(7); + + /* Enable BCS for Headset mic */ + if (event == SND_SOC_DAPM_PRE_PMU && strnstr(w->name, "ADC", sizeof("ADC"))) + if (w->shift == 1 && !use_amic3) + set_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + atomic_inc(&wcd937x->ana_clk_count); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(4), BIT(4)); + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask)) + clear_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); + + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, BIT(3), 0x00); + break; + } + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_REQ_CTL, BIT(1), BIT(1)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_REQ_CTL, BIT(0), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(6), BIT(6)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3_HPF, BIT(6), BIT(6)); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, 0x70, 0x70); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH1, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(6), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3, BIT(7), BIT(7)); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH1, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH2, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_ANA_TX_CH3, BIT(7), 0x00); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, BIT(4), 0x00); + + atomic_dec(&wcd937x->ana_clk_count); + if (atomic_read(&wcd937x->ana_clk_count) <= 0) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), 0x00); + atomic_set(&wcd937x->ana_clk_count, 0); + } + + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(7), 0x00); + break; + } + + return 0; +} + +static int wcd937x_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; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC1_CTL; + break; + case 2: + case 3: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC2_CTL; + break; + case 4: + case 5: + dmic_clk_reg = WCD937X_DIGITAL_CDC_DMIC3_CTL; + break; + default: + dev_err(component->dev, "Invalid DMIC Selection\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + BIT(7), BIT(7)); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x07, BIT(1)); + snd_soc_component_update_bits(component, + dmic_clk_reg, BIT(3), BIT(3)); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x70, BIT(5)); + break; + } + + return 0; +} + +static int wcd937x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + if (micb_index < 0 || (micb_index > WCD937X_MAX_MICBIAS - 1)) { + dev_err(component->dev, "Invalid micbias index, micb_ind:%d\n", micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + dev_err(component->dev, "Invalid micbias number: %d\n", micb_num); + return -EINVAL; + } + + mutex_lock(&wcd937x->micb_lock); + switch (req) { + case MICB_PULLUP_ENABLE: + wcd937x->pullup_ref[micb_index]++; + if (wcd937x->pullup_ref[micb_index] == 1 && + wcd937x->micb_ref[micb_index] == 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, BIT(7)); + break; + case MICB_PULLUP_DISABLE: + if (wcd937x->pullup_ref[micb_index] > 0) + wcd937x->pullup_ref[micb_index]++; + if (wcd937x->pullup_ref[micb_index] == 0 && + wcd937x->micb_ref[micb_index] == 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, 0x00); + break; + case MICB_ENABLE: + wcd937x->micb_ref[micb_index]++; + atomic_inc(&wcd937x->ana_clk_count); + if (wcd937x->micb_ref[micb_index] == 1) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_DIG_CLK_CTL, + 0xf0, 0xf0); + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), BIT(4)); + snd_soc_component_update_bits(component, + WCD937X_MICB1_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_MICB2_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + WCD937X_MICB3_TEST_CTL_2, + BIT(0), BIT(0)); + snd_soc_component_update_bits(component, + micb_reg, 0xc0, BIT(6)); + + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + } + break; + case MICB_DISABLE: + atomic_dec(&wcd937x->ana_clk_count); + if (wcd937x->micb_ref[micb_index] > 0) + wcd937x->micb_ref[micb_index]--; + if (wcd937x->micb_ref[micb_index] == 0 && + wcd937x->pullup_ref[micb_index] > 0) + snd_soc_component_update_bits(component, micb_reg, + 0xc0, BIT(7)); + else if (wcd937x->micb_ref[micb_index] == 0 && + wcd937x->pullup_ref[micb_index] == 0) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_update_bits(component, micb_reg, + 0xc0, 0x00); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd937x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + if (atomic_read(&wcd937x->ana_clk_count) <= 0) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_ANA_CLK_CTL, + BIT(4), 0x00); + atomic_set(&wcd937x->ana_clk_count, 0); + } + break; + } + mutex_unlock(&wcd937x->micb_lock); + + return 0; +} + +static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + 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: + wcd937x_micbias_control(component, micb_num, + MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_micbias_control(component, micb_num, + MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __wcd937x_codec_enable_micbias(w, event); +} + +static int __wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + 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: + wcd937x_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd937x_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __wcd937x_codec_enable_micbias_pullup(w, event); +} + +static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable) +{ + struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1]; + const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; + u8 port_num = ch_info->port_num; + u8 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 wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd937x->hph_mode; + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (!mode_val) + mode_val = CLS_AB; + + if (mode_val == wcd937x->hph_mode) + return 0; + + 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_LP: + case CLS_AB_HIFI: + wcd937x->hph_mode = mode_val; + return 1; + } + + dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__); + return -EINVAL; +} + +static int wcd937x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + ucontrol->value.integer.value[0] = hphr ? wcd937x->comp2_enable : + wcd937x->comp1_enable; + return 0; +} + +static int wcd937x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[AIF1_PB]; + int value = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc; + int portidx; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + if (hphr) { + if (value == wcd937x->comp2_enable) + return 0; + + wcd937x->comp2_enable = value; + } else { + if (value == wcd937x->comp1_enable) + return 0; + + wcd937x->comp1_enable = value; + } + + portidx = wcd->ch_info[mc->reg].port_num; + + if (value) + wcd937x_connect_port(wcd, portidx, mc->reg, true); + else + wcd937x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp); + struct wcd937x_sdw_priv *wcd; + int dai_id = mixer->shift; + int ch_idx = mixer->reg; + int portidx; + + wcd = wcd937x->sdw_priv[dai_id]; + portidx = wcd->ch_info[ch_idx].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx]; + + return 0; +} + +static int wcd937x_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 wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(comp); + struct wcd937x_sdw_priv *wcd; + int dai_id = mixer->shift; + int ch_idx = mixer->reg; + int portidx; + bool enable; + + wcd = wcd937x->sdw_priv[dai_id]; + + portidx = wcd->ch_info[ch_idx].port_num; + + enable = ucontrol->value.integer.value[0]; + + if (enable == wcd->port_enable[portidx]) { + wcd937x_connect_port(wcd, portidx, ch_idx, enable); + return 0; + } + + wcd->port_enable[portidx] = enable; + wcd937x_connect_port(wcd, portidx, ch_idx, enable); + + return 1; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_NORMAL", "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_LP", "CLS_AB_HIFI", +}; + +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); + +/* MBHC related */ +static void wcd937x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_1, + WCD937X_MBHC_CTL_RCO_EN_MASK, enable); +} + +static void wcd937x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_ELECT, + WCD937X_ANA_MBHC_BIAS_EN, enable); +} + +static void wcd937x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_BTN0 + i, + WCD937X_MBHC_BTN_VTH_MASK, vth); + } +} + +static bool wcd937x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = snd_soc_component_read_field(component, + WCD937X_ANA_MICB2, + WCD937X_ANA_MICB2_ENABLE_MASK); + if (val == WCD937X_MICB_ENABLE) + return true; + } + return false; +} + +static void wcd937x_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_2P0_UA; + + snd_soc_component_write_field(component, + WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD937X_HSDET_PULLUP_C_MASK, pull_up_cur); +} + +static int wcd937x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd937x_micbias_control(component, micb_num, req, false); +} + +static void wcd937x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_SHIFT_CTRL_MASK, 0x0C); + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_EN_MASK, 0); + snd_soc_component_write_field(component, WCD937X_ANA_MICB2_RAMP, + WCD937X_RAMP_SHIFT_CTRL_MASK, 0); + } +} + +static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD937X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD937X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD937X_ANA_MICB3; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd937x->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, + WCD937X_MICB_EN_MASK); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + WCD937X_MICB_VOUT_MASK); + + req_vout_ctl = wcd937x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + if (micb_en == WCD937X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_EN_MASK, + WCD937X_MICB_PULL_UP); + + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_VOUT_MASK, + req_vout_ctl); + + if (micb_en == WCD937X_MICB_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + WCD937X_MICB_EN_MASK, + WCD937X_MICB_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd937x->micb_lock); + return ret; +} + +static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd937x_priv *wcd937x = 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 (wcd937x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd937x->micb2_mv; + + return wcd937x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +static void wcd937x_mbhc_get_result_params(struct snd_soc_component *component, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int i; + int val, val1; + s16 c1; + s32 x1, d1; + s32 denom; + static const int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD937X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd937x->regmap, WCD937X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD937X_MBHC_GET_X1(val); + c1 = WCD937X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if (c1 < 2 && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n", + c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD937X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD937X_ZDET_FLOATING_IMPEDANCE; + + dev_err(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) { + regmap_read(wcd937x->regmap, + WCD937X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd937x->regmap, + WCD937X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x08; + val |= val1; + x1 = WCD937X_MBHC_GET_X1(val); + i++; + if (i == WCD937X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd937x_mbhc_zdet_ramp(struct snd_soc_component *component, + struct wcd937x_mbhc_zdet_param *zdet_param, + s32 *zl, s32 *zr, s16 *d1_a) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + s32 zdet = 0; + + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, + WCD937X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN5, + WCD937X_VTH_MASK, zdet_param->btn5); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN6, + WCD937X_VTH_MASK, zdet_param->btn6); + snd_soc_component_update_bits(component, WCD937X_ANA_MBHC_BTN7, + WCD937X_VTH_MASK, zdet_param->btn7); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, + WCD937X_ZDET_RANGE_CTL_MASK, zdet_param->noff); + snd_soc_component_update_bits(component, WCD937X_MBHC_NEW_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x80); + wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x40); + wcd937x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static void wcd937x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + s32 *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD937X_ZDET_VAL_400 / 1000)) + q1 = snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd937x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + u32 *zl, u32 *zr) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + s16 reg0, reg1, reg2, reg3, reg4; + s32 z1l, z1r, z1ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd937x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd937x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + reg0 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD937X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD937X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read(component, WCD937X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd937x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x00); + + /* Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + regmap_update_bits(wcd937x->regmap, + WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00); + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1); + + if (!WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1l)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1l < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1l > WCD937X_ZDET_VAL_400) && + (z1l <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1l > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1l, NULL, d1); + +left_ch_impedance: + if (z1l == WCD937X_ZDET_FLOATING_IMPEDANCE || + z1l > WCD937X_ZDET_VAL_100K) { + *zl = WCD937X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1l / 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + + /* Start of right impedance ramp and calculation */ + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1); + if (WCD937X_MBHC_IS_SECOND_RAMP_REQUIRED(z1r)) { + if ((z1r > WCD937X_ZDET_VAL_1200 && + zdet_param_ptr->noff == 0x6) || + ((*zl) != WCD937X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1r < WCD937X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1r > WCD937X_ZDET_VAL_400) && + (z1r <= WCD937X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1r > WCD937X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd937x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1r, d1); + } +right_ch_impedance: + if (z1r == WCD937X_ZDET_FLOATING_IMPEDANCE || + z1r > WCD937X_ZDET_VAL_100K) { + *zr = WCD937X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1r / 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + + /* Mono/stereo detection */ + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD937X_ZDET_FLOATING_IMPEDANCE)) { + dev_err(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD937X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD937X_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))) { + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST, + WCD937X_HPHPA_GND_OVR_MASK, 1); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, 1); + if (*zl < (WCD937X_ZDET_VAL_32 / 1000)) + wcd937x_mbhc_zdet_ramp(component, &zdet_param[0], &z1ls, NULL, d1); + else + wcd937x_mbhc_zdet_ramp(component, &zdet_param[1], &z1ls, NULL, d1); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, 0); + snd_soc_component_write_field(component, WCD937X_HPH_R_ATEST, + WCD937X_HPHPA_GND_OVR_MASK, 0); + z1ls /= 1000; + wcd937x_wcd_mbhc_qfuse_cal(component, &z1ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1ls > zMono) ? (z1ls - zMono) : (zMono - z1ls); + z_diff2 = ((*zl) > z1ls) ? ((*zl) - z1ls) : (z1ls - (*zl)); + if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + zMono))) + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + else + wcd_mbhc_set_hph_type(wcd937x->wcd_mbhc, WCD_MBHC_HPH_MONO); + + /* Enable surge protection again after impedance detection */ + regmap_update_bits(wcd937x->regmap, + WCD937X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); +zdet_complete: + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD937X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd937x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, WCD937X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD937X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd937x->regmap, + WCD937X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd937x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_HSG_PULLUP_COMP_EN, 1); + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_GND_DET_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_GND_DET_EN_MASK, 0); + snd_soc_component_write_field(component, WCD937X_ANA_MBHC_MECH, + WCD937X_MBHC_HSG_PULLUP_COMP_EN, 0); + } +} + +static void wcd937x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_R_MASK, enable); + snd_soc_component_write_field(component, WCD937X_HPH_PA_CTL2, + WCD937X_HPHPA_GND_L_MASK, enable); +} + +static void wcd937x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (wcd937x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd937x->mbhc_cfg.hphl_swh) { + dev_err(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref); +} + +static void wcd937x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, wcd937x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); +} + +static bool wcd937x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd937x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd937x->mbhc_cfg.hphl_swh) { + dev_err(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD937X_MBHC_NEW_CTL_2, + WCD937X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD937X_MBHC_NEW_CTL_2, WCD937X_M_RTH_CTL_MASK)) + goto done; + + wcd937x_mbhc_moisture_detect_en(component, true); + /* Read moisture comparator status */ + ret = ((snd_soc_component_read(component, WCD937X_MBHC_NEW_FSM_STATUS) + & 0x20) ? 0 : 1); +done: + return ret; +} + +static void wcd937x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD937X_MOISTURE_EN_POLLING_MASK, enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd937x_mbhc_clk_setup, + .mbhc_bias = wcd937x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd937x_mbhc_program_btn_thr, + .micbias_enable_status = wcd937x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd937x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd937x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd937x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd937x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd937x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd937x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd937x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd937x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd937x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd937x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd937x_mbhc_moisture_detect_en, +}; + +static int wcd937x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd937x->wcd_mbhc); + + return 0; +} + +static int wcd937x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 zl, zr; + bool hphr; + struct soc_mixer_control *mc; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(wcd937x->wcd_mbhc, &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, WCD_MBHC_HPH_STEREO, 0, + wcd937x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, + wcd937x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, + wcd937x_hph_impedance_get, NULL), +}; + +static int wcd937x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd937x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHR_OCP_INT); + + wcd937x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd937x->wcd_mbhc)) + return PTR_ERR(wcd937x->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 wcd937x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd937x->wcd_mbhc); +} + +/* END MBHC */ + +static const struct snd_kcontrol_new wcd937x_snd_controls[] = { + SOC_SINGLE_TLV("EAR_PA Volume", WCD937X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd937x_rx_hph_mode_get, wcd937x_rx_hph_mode_put), + + SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0, + wcd937x_get_compander, wcd937x_set_compander), + + SOC_SINGLE_TLV("HPHL Volume", WCD937X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD937X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("ADC1 Volume", WCD937X_ANA_TX_CH1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD937X_ANA_TX_CH2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD937X_ANA_TX_CH3, 0, 20, 0, analog_gain), + + SOC_SINGLE_EXT("HPHL Switch", WCD937X_HPH_L, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD937X_HPH_R, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + + SOC_SINGLE_EXT("ADC1 Switch", WCD937X_ADC1, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD937X_ADC2, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD937X_ADC3, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD937X_DMIC0, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD937X_DMIC1, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD937X_MBHC, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD937X_DMIC2, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD937X_DMIC3, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD937X_DMIC4, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD937X_DMIC5, 1, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), +}; + +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 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 ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new aux_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 adc2_mux_text[] = { + "INP2", "INP3" +}; + +static const char * const rdac3_mux_text[] = { + "RX1", "RX3" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD937X_TX_NEW_TX_CH2_SEL, 7, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD937X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const struct snd_kcontrol_new rx_rdac3_mux = SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { + /* Input widgets */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_AUX"), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_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, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + + /* TX mixers */ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* MIC_BIAS widgets */ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd937x_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, + wcd937x_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, + wcd937x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + /* RX widgets */ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0, + wcd937x_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("AUX PGA", WCD937X_AUX_AUXPA, 7, 0, NULL, 0, + wcd937x_codec_enable_aux_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", WCD937X_ANA_HPH, 7, 0, NULL, 0, + wcd937x_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", WCD937X_ANA_HPH, 6, 0, NULL, 0, + wcd937x_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, + wcd937x_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, + wcd937x_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, + wcd937x_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_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_codec_aux_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_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx1, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx2, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd937x_enable_rx3, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* 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("AUX_RDAC", SND_SOC_NOPM, 0, 0, + aux_rdac_switch, ARRAY_SIZE(aux_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)), + + /* TX output widgets */ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("WCD_TX_OUTPUT"), + + /* RX output widgets */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("AUX"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + + /* MIC_BIAS pull up widgets */ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd937x_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, + wcd937x_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, + wcd937x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = { + /* Input widgets */ + SND_SOC_DAPM_INPUT("AMIC4"), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd937x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd937x_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd937x_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, + wcd937x_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, + wcd937x_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, + wcd937x_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, + wcd937x_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, + wcd937x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX mixer widgets */ + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, + 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1, + 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 2, + 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 3, + 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 4, + 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 5, + 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), + wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 2, 0, adc3_switch, + ARRAY_SIZE(adc3_switch), wcd937x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output widgets */ + 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"), +}; + +static const struct snd_soc_dapm_route wcd937x_audio_map[] = { + { "ADC1_OUTPUT", NULL, "ADC1_MIXER" }, + { "ADC1_MIXER", "Switch", "ADC1 REQ" }, + { "ADC1 REQ", NULL, "ADC1" }, + { "ADC1", NULL, "AMIC1" }, + + { "ADC2_OUTPUT", NULL, "ADC2_MIXER" }, + { "ADC2_MIXER", "Switch", "ADC2 REQ" }, + { "ADC2 REQ", NULL, "ADC2" }, + { "ADC2", NULL, "ADC2 MUX" }, + { "ADC2 MUX", "INP3", "AMIC3" }, + { "ADC2 MUX", "INP2", "AMIC2" }, + + { "IN1_HPHL", NULL, "VDD_BUCK" }, + { "IN1_HPHL", NULL, "CLS_H_PORT" }, + { "RX1", NULL, "IN1_HPHL" }, + { "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" }, + { "HPHR_RDAC", "Switch", "RDAC2" }, + { "HPHR PGA", NULL, "HPHR_RDAC" }, + { "HPHR", NULL, "HPHR PGA" }, + + { "IN3_AUX", NULL, "VDD_BUCK" }, + { "IN3_AUX", NULL, "CLS_H_PORT" }, + { "RX3", NULL, "IN3_AUX" }, + { "RDAC4", NULL, "RX3" }, + { "AUX_RDAC", "Switch", "RDAC4" }, + { "AUX PGA", NULL, "AUX_RDAC" }, + { "AUX", NULL, "AUX PGA" }, + + { "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 const struct snd_soc_dapm_route wcd9375_audio_map[] = { + { "ADC3_OUTPUT", NULL, "ADC3_MIXER" }, + { "ADC3_OUTPUT", NULL, "ADC3_MIXER" }, + { "ADC3_MIXER", "Switch", "ADC3 REQ" }, + { "ADC3 REQ", NULL, "ADC3" }, + { "ADC3", NULL, "AMIC4" }, + + { "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" }, +}; + +static int wcd937x_set_micbias_data(struct wcd937x_priv *wcd937x) +{ + int vout_ctl[3]; + + /* Set micbias voltage */ + vout_ctl[0] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb1_mv); + vout_ctl[1] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb2_mv); + vout_ctl[2] = wcd937x_get_micb_vout_ctl_val(wcd937x->micb3_mv); + if ((vout_ctl[0] | vout_ctl[1] | vout_ctl[2]) < 0) + return -EINVAL; + + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB1, WCD937X_ANA_MICB_VOUT, vout_ctl[0]); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB2, WCD937X_ANA_MICB_VOUT, vout_ctl[1]); + regmap_update_bits(wcd937x->regmap, WCD937X_ANA_MICB3, WCD937X_ANA_MICB_VOUT, vout_ctl[2]); + + return 0; +} + +static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static const struct irq_chip wcd_irq_chip = { + .name = "WCD937x", +}; + +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 wcd937x_irq_init(struct wcd937x_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, &wcd937x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd937x_soc_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd937x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int i, ret; + u32 chipid; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(5000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd937x->regmap); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + chipid = (snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; + if (chipid != CHIPID_WCD9370 && chipid != CHIPID_WCD9375) { + dev_err(dev, "Got unknown chip id: 0x%x\n", chipid); + pm_runtime_put(dev); + return -EINVAL; + } + + wcd937x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD937X); + if (IS_ERR(wcd937x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd937x->clsh_info); + } + + wcd937x_io_init(wcd937x->regmap); + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd937x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd937x->regmap, (WCD937X_DIGITAL_INTR_LEVEL_0 + i), 0); + + pm_runtime_put(dev); + + wcd937x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHR_PDM_WD_INT); + wcd937x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_HPHL_PDM_WD_INT); + wcd937x->aux_pdm_wd_int = regmap_irq_get_virq(wcd937x->irq_chip, + WCD937X_IRQ_AUX_PDM_WD_INT); + + /* Request for watchdog interrupt */ + ret = devm_request_threaded_irq(dev, wcd937x->hphr_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request HPHR watchdog interrupt (%d)\n", ret); + + ret = devm_request_threaded_irq(dev, wcd937x->hphl_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request HPHL watchdog interrupt (%d)\n", ret); + + ret = devm_request_threaded_irq(dev, wcd937x->aux_pdm_wd_int, NULL, wcd937x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WDOG INT", wcd937x); + if (ret) + dev_err(dev, "Failed to request Aux watchdog interrupt (%d)\n", ret); + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd937x->hphr_pdm_wd_int); + disable_irq_nosync(wcd937x->hphl_pdm_wd_int); + disable_irq_nosync(wcd937x->aux_pdm_wd_int); + + if (chipid == CHIPID_WCD9375) { + ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets, + ARRAY_SIZE(wcd9375_dapm_widgets)); + if (ret < 0) { + dev_err(component->dev, "Failed to add snd_ctls\n"); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, wcd9375_audio_map, + ARRAY_SIZE(wcd9375_audio_map)); + if (ret < 0) { + dev_err(component->dev, "Failed to add routes\n"); + return ret; + } + } + + ret = wcd937x_mbhc_init(component); + if (ret) + dev_err(component->dev, "mbhc initialization failed\n"); + + return ret; +} + +static void wcd937x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + + wcd937x_mbhc_deinit(component); + free_irq(wcd937x->aux_pdm_wd_int, wcd937x); + free_irq(wcd937x->hphl_pdm_wd_int, wcd937x); + free_irq(wcd937x->hphr_pdm_wd_int, wcd937x); + + wcd_clsh_ctrl_free(wcd937x->clsh_info); +} + +static int wcd937x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd937x_priv *wcd = dev_get_drvdata(comp->dev); + int ret = 0; + + if (jack) + ret = wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + else + wcd_mbhc_stop(wcd->wcd_mbhc); + + return ret; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd937x = { + .name = "wcd937x_codec", + .probe = wcd937x_soc_codec_probe, + .remove = wcd937x_soc_codec_remove, + .controls = wcd937x_snd_controls, + .num_controls = ARRAY_SIZE(wcd937x_snd_controls), + .dapm_widgets = wcd937x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd937x_dapm_widgets), + .dapm_routes = wcd937x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd937x_audio_map), + .set_jack = wcd937x_codec_set_jack, + .endianness = 1, +}; + +static void wcd937x_dt_parse_micbias_info(struct device *dev, struct wcd937x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int ret = 0; + + ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!ret) + wcd->micb1_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias1 DT property not found\n"); + + ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!ret) + wcd->micb2_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias2 DT property not found\n"); + + ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!ret) + wcd->micb3_mv = prop_val / 1000; + else + dev_warn(dev, "Micbias3 DT property not found\n"); +} + +static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + int value; + struct wcd937x_priv *wcd937x; + + wcd937x = snd_soc_component_get_drvdata(component); + + value = gpiod_get_value(wcd937x->us_euro_gpio); + gpiod_set_value(wcd937x->us_euro_gpio, !value); + + return true; +} + +static int wcd937x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + return wcd937x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd937x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + return sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); +} + +static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + + wcd->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = { + .hw_params = wcd937x_codec_hw_params, + .hw_free = wcd937x_codec_free, + .set_stream = wcd937x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd937x_dais[] = { + [0] = { + .name = "wcd937x-sdw-rx", + .playback = { + .stream_name = "WCD AIF Playback", + .rates = WCD937X_RATES | WCD937X_FRAC_RATES, + .formats = WCD937X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd937x_sdw_dai_ops, + }, + [1] = { + .name = "wcd937x-sdw-tx", + .capture = { + .stream_name = "WCD AIF Capture", + .rates = WCD937X_RATES, + .formats = WCD937X_FORMATS, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd937x_sdw_dai_ops, + }, +}; + +static int wcd937x_bind(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + int ret; + + /* Give the SDW subdevices some more time to settle */ + usleep_range(5000, 5010); + + ret = component_bind_all(dev, wcd937x); + if (ret) { + dev_err(dev, "Slave bind failed, ret = %d\n", ret); + return ret; + } + + wcd937x->rxdev = wcd937x_sdw_device_get(wcd937x->rxnode); + if (!wcd937x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + return -EINVAL; + } + + wcd937x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd937x->rxdev); + wcd937x->sdw_priv[AIF1_PB]->wcd937x = wcd937x; + + wcd937x->txdev = wcd937x_sdw_device_get(wcd937x->txnode); + if (!wcd937x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + return -EINVAL; + } + + wcd937x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd937x->txdev); + wcd937x->sdw_priv[AIF1_CAP]->wcd937x = wcd937x; + wcd937x->tx_sdw_dev = dev_to_sdw_dev(wcd937x->txdev); + if (!wcd937x->tx_sdw_dev) { + dev_err(dev, "could not get txslave with matching of dev\n"); + return -EINVAL; + } + + /* + * As TX is the main CSR reg interface, which should not be suspended first. + * expicilty add the dependency link + */ + if (!device_link_add(wcd937x->rxdev, wcd937x->txdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink TX and RX\n"); + return -EINVAL; + } + + if (!device_link_add(dev, wcd937x->txdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink WCD and TX\n"); + return -EINVAL; + } + + if (!device_link_add(dev, wcd937x->rxdev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "Could not devlink WCD and RX\n"); + return -EINVAL; + } + + wcd937x->regmap = dev_get_regmap(&wcd937x->tx_sdw_dev->dev, NULL); + if (!wcd937x->regmap) { + dev_err(dev, "could not get TX device regmap\n"); + return -EINVAL; + } + + ret = wcd937x_irq_init(wcd937x, dev); + if (ret) { + dev_err(dev, "IRQ init failed: %d\n", ret); + return ret; + } + + wcd937x->sdw_priv[AIF1_PB]->slave_irq = wcd937x->virq; + wcd937x->sdw_priv[AIF1_CAP]->slave_irq = wcd937x->virq; + + ret = wcd937x_set_micbias_data(wcd937x); + if (ret < 0) { + dev_err(dev, "Bad micbias pdata\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd937x, + wcd937x_dais, ARRAY_SIZE(wcd937x_dais)); + if (ret) + dev_err(dev, "Codec registration failed\n"); + + return ret; +} + +static void wcd937x_unbind(struct device *dev) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd937x->txdev); + device_link_remove(dev, wcd937x->rxdev); + device_link_remove(wcd937x->rxdev, wcd937x->txdev); + component_unbind_all(dev, wcd937x); + mutex_destroy(&wcd937x->micb_lock); +} + +static const struct component_master_ops wcd937x_comp_ops = { + .bind = wcd937x_bind, + .unbind = wcd937x_unbind, +}; + +static int wcd937x_add_slave_components(struct wcd937x_priv *wcd937x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np = dev->of_node; + + wcd937x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd937x->rxnode) { + dev_err(dev, "Couldn't parse phandle to qcom,rx-device!\n"); + return -ENODEV; + } + of_node_get(wcd937x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd937x->rxnode); + + wcd937x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd937x->txnode) { + dev_err(dev, "Couldn't parse phandle to qcom,tx-device\n"); + return -ENODEV; + } + of_node_get(wcd937x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd937x->txnode); + + return 0; +} + +static int wcd937x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct device *dev = &pdev->dev; + struct wcd937x_priv *wcd937x; + struct wcd_mbhc_config *cfg; + int ret; + + wcd937x = devm_kzalloc(dev, sizeof(*wcd937x), GFP_KERNEL); + if (!wcd937x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd937x); + mutex_init(&wcd937x->micb_lock); + + wcd937x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(wcd937x->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd937x->reset_gpio), + "failed to reset wcd gpio\n"); + + wcd937x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW); + if (IS_ERR(wcd937x->us_euro_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd937x->us_euro_gpio), + "us-euro swap Control GPIO not found\n"); + + cfg = &wcd937x->mbhc_cfg; + cfg->swap_gnd_mic = wcd937x_swap_gnd_mic; + + wcd937x->supplies[0].supply = "vdd-rxtx"; + wcd937x->supplies[1].supply = "vdd-px"; + wcd937x->supplies[2].supply = "vdd-mic-bias"; + wcd937x->supplies[3].supply = "vdd-buck"; + + ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + if (ret) { + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + } + + wcd937x_dt_parse_micbias_info(dev, wcd937x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD937X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd937x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, &wcd937x->mbhc_cfg); + + ret = wcd937x_add_slave_components(wcd937x, dev, &match); + if (ret) + goto err_disable_regulators; + + wcd937x_reset(wcd937x); + + ret = component_master_add_with_match(dev, &wcd937x_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; + +err_disable_regulators: + regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + + return ret; +} + +static void wcd937x_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); + + component_master_del(&pdev->dev, &wcd937x_comp_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); +} + +#if defined(CONFIG_OF) +static const struct of_device_id wcd937x_of_match[] = { + { .compatible = "qcom,wcd9370-codec" }, + { .compatible = "qcom,wcd9375-codec" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd937x_of_match); +#endif + +static struct platform_driver wcd937x_codec_driver = { + .probe = wcd937x_probe, + .remove_new = wcd937x_remove, + .driver = { + .name = "wcd937x_codec", + .of_match_table = of_match_ptr(wcd937x_of_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd937x_codec_driver); +MODULE_DESCRIPTION("WCD937X Codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h new file mode 100644 index 000000000000..37bff16e88dd --- /dev/null +++ b/sound/soc/codecs/wcd937x.h @@ -0,0 +1,624 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _WCD937X_REGISTERS_H +#define _WCD937X_REGISTERS_H + +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> + +#define WCD937X_BASE_ADDRESS 0x3000 +#define WCD937X_ANA_BIAS 0x3001 +#define WCD937X_ANA_RX_SUPPLIES 0x3008 +#define WCD937X_ANA_HPH 0x3009 +#define WCD937X_ANA_EAR 0x300A +#define WCD937X_ANA_EAR_COMPANDER_CTL 0x300B +#define WCD937X_EAR_GAIN_MASK GENMASK(6, 2) +#define WCD937X_ANA_TX_CH1 0x300E +#define WCD937X_ANA_TX_CH2 0x300F +#define WCD937X_ANA_TX_CH3 0x3010 +#define WCD937X_ANA_TX_CH3_HPF 0x3011 +#define WCD937X_ANA_MICB1_MICB2_DSP_EN_LOGIC 0x3012 +#define WCD937X_ANA_MICB3_DSP_EN_LOGIC 0x3013 +#define WCD937X_ANA_MBHC_MECH 0x3014 +#define WCD937X_MBHC_L_DET_EN_MASK BIT(7) +#define WCD937X_MBHC_L_DET_EN BIT(7) +#define WCD937X_MBHC_GND_DET_EN_MASK BIT(6) +#define WCD937X_MBHC_MECH_DETECT_TYPE_MASK BIT(5) +#define WCD937X_MBHC_MECH_DETECT_TYPE_INS 1 +#define WCD937X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4) +#define WCD937X_MBHC_HPHL_PLUG_TYPE_NO 1 +#define WCD937X_MBHC_GND_PLUG_TYPE_MASK BIT(3) +#define WCD937X_MBHC_GND_PLUG_TYPE_NO 1 +#define WCD937X_MBHC_HSL_PULLUP_COMP_EN BIT(2) +#define WCD937X_MBHC_HSG_PULLUP_COMP_EN BIT(1) +#define WCD937X_MBHC_HPHL_100K_TO_GND_EN BIT(0) +#define WCD937X_ANA_MBHC_ELECT 0x3015 +#define WCD937X_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4) +#define WCD937X_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4) +#define WCD937X_ANA_MBHC_BD_ISRC_OFF 0 +#define WCD937X_ANA_MBHC_BIAS_EN_MASK BIT(0) +#define WCD937X_ANA_MBHC_BIAS_EN BIT(0) +#define WCD937X_ANA_MBHC_ZDET 0x3016 +#define WCD937X_ANA_MBHC_RESULT_1 0x3017 +#define WCD937X_ANA_MBHC_RESULT_2 0x3018 +#define WCD937X_ANA_MBHC_RESULT_3 0x3019 +#define WCD937X_MBHC_BTN_RESULT_MASK GENMASK(2, 0) +#define WCD937X_ANA_MBHC_BTN0 0x301A +#define WCD937X_MBHC_BTN_VTH_MASK GENMASK(7, 2) +#define WCD937X_ANA_MBHC_BTN1 0x301B +#define WCD937X_ANA_MBHC_BTN2 0x301C +#define WCD937X_ANA_MBHC_BTN3 0x301D +#define WCD937X_ANA_MBHC_BTN4 0x301E +#define WCD937X_ANA_MBHC_BTN5 0x301F +#define WCD937X_VTH_MASK GENMASK(7, 2) +#define WCD937X_ANA_MBHC_BTN6 0x3020 +#define WCD937X_ANA_MBHC_BTN7 0x3021 +#define WCD937X_ANA_MICB1 0x3022 +#define WCD937X_MICB_VOUT_MASK GENMASK(5, 0) +#define WCD937X_MICB_EN_MASK GENMASK(7, 6) +#define WCD937X_MICB_DISABLE 0 +#define WCD937X_MICB_ENABLE 1 +#define WCD937X_MICB_PULL_UP 2 +#define WCD937X_MICB_PULL_DOWN 3 +#define WCD937X_ANA_MICB2 0x3023 +#define WCD937X_ANA_MICB2_ENABLE BIT(6) +#define WCD937X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) +#define WCD937X_ANA_MICB2_VOUT_MASK GENMASK(5, 0) +#define WCD937X_ANA_MICB2_RAMP 0x3024 +#define WCD937X_RAMP_EN_MASK BIT(7) +#define WCD937X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) +#define WCD937X_ANA_MICB3 0x3025 +#define WCD937X_ANA_MICB_EN GENMASK(7, 6) +#define WCD937X_MICB_DISABLE 0 +#define WCD937X_MICB_ENABLE 1 +#define WCD937X_MICB_PULL_UP 2 +#define WCD937X_ANA_MICB_VOUT GENMASK(5, 0) +#define WCD937X_BIAS_CTL 0x3028 +#define WCD937X_BIAS_VBG_FINE_ADJ 0x3029 +#define WCD937X_LDOL_VDDCX_ADJUST 0x3040 +#define WCD937X_LDOL_DISABLE_LDOL 0x3041 +#define WCD937X_MBHC_CTL_CLK 0x3056 +#define WCD937X_MBHC_CTL_ANA 0x3057 +#define WCD937X_MBHC_CTL_SPARE_1 0x3058 +#define WCD937X_MBHC_CTL_SPARE_2 0x3059 +#define WCD937X_MBHC_CTL_BCS 0x305A +#define WCD937X_MBHC_MOISTURE_DET_FSM_STATUS 0x305B +#define WCD937X_MBHC_TEST_CTL 0x305C +#define WCD937X_LDOH_MODE 0x3067 +#define WCD937X_LDOH_BIAS 0x3068 +#define WCD937X_LDOH_STB_LOADS 0x3069 +#define WCD937X_LDOH_SLOWRAMP 0x306A +#define WCD937X_MICB1_TEST_CTL_1 0x306B +#define WCD937X_MICB1_TEST_CTL_2 0x306C +#define WCD937X_MICB1_TEST_CTL_3 0x306D +#define WCD937X_MICB2_TEST_CTL_1 0x306E +#define WCD937X_MICB2_TEST_CTL_2 0x306F +#define WCD937X_MICB2_TEST_CTL_3 0x3070 +#define WCD937X_MICB3_TEST_CTL_1 0x3071 +#define WCD937X_MICB3_TEST_CTL_2 0x3072 +#define WCD937X_MICB3_TEST_CTL_3 0x3073 +#define WCD937X_TX_COM_ADC_VCM 0x3077 +#define WCD937X_TX_COM_BIAS_ATEST 0x3078 +#define WCD937X_TX_COM_ADC_INT1_IB 0x3079 +#define WCD937X_TX_COM_ADC_INT2_IB 0x307A +#define WCD937X_TX_COM_TXFE_DIV_CTL 0x307B +#define WCD937X_TX_COM_TXFE_DIV_START 0x307C +#define WCD937X_TX_COM_TXFE_DIV_STOP_9P6M 0x307D +#define WCD937X_TX_COM_TXFE_DIV_STOP_12P288M 0x307E +#define WCD937X_TX_1_2_TEST_EN 0x307F +#define WCD937X_TX_1_2_ADC_IB 0x3080 +#define WCD937X_TX_1_2_ATEST_REFCTL 0x3081 +#define WCD937X_TX_1_2_TEST_CTL 0x3082 +#define WCD937X_TX_1_2_TEST_BLK_EN 0x3083 +#define WCD937X_TX_1_2_TXFE_CLKDIV 0x3084 +#define WCD937X_TX_1_2_SAR2_ERR 0x3085 +#define WCD937X_TX_1_2_SAR1_ERR 0x3086 +#define WCD937X_TX_3_TEST_EN 0x3087 +#define WCD937X_TX_3_ADC_IB 0x3088 +#define WCD937X_TX_3_ATEST_REFCTL 0x3089 +#define WCD937X_TX_3_TEST_CTL 0x308A +#define WCD937X_TX_3_TEST_BLK_EN 0x308B +#define WCD937X_TX_3_TXFE_CLKDIV 0x308C +#define WCD937X_TX_3_SPARE_MONO 0x308D +#define WCD937X_TX_3_SAR1_ERR 0x308E +#define WCD937X_CLASSH_MODE_1 0x3097 +#define WCD937X_CLASSH_MODE_2 0x3098 +#define WCD937X_CLASSH_MODE_3 0x3099 +#define WCD937X_CLASSH_CTRL_VCL_1 0x309A +#define WCD937X_CLASSH_CTRL_VCL_2 0x309B +#define WCD937X_CLASSH_CTRL_CCL_1 0x309C +#define WCD937X_CLASSH_CTRL_CCL_2 0x309D +#define WCD937X_CLASSH_CTRL_CCL_3 0x309E +#define WCD937X_CLASSH_CTRL_CCL_4 0x309F +#define WCD937X_CLASSH_CTRL_CCL_5 0x30A0 +#define WCD937X_CLASSH_BUCK_TMUX_A_D 0x30A1 +#define WCD937X_CLASSH_BUCK_SW_DRV_CNTL 0x30A2 +#define WCD937X_CLASSH_SPARE 0x30A3 +#define WCD937X_FLYBACK_EN 0x30A4 +#define WCD937X_FLYBACK_VNEG_CTRL_1 0x30A5 +#define WCD937X_FLYBACK_VNEG_CTRL_2 0x30A6 +#define WCD937X_FLYBACK_VNEG_CTRL_3 0x30A7 +#define WCD937X_FLYBACK_VNEG_CTRL_4 0x30A8 +#define WCD937X_FLYBACK_VNEG_CTRL_5 0x30A9 +#define WCD937X_FLYBACK_VNEG_CTRL_6 0x30AA +#define WCD937X_FLYBACK_VNEG_CTRL_7 0x30AB +#define WCD937X_FLYBACK_VNEG_CTRL_8 0x30AC +#define WCD937X_FLYBACK_VNEG_CTRL_9 0x30AD +#define WCD937X_FLYBACK_VNEGDAC_CTRL_1 0x30AE +#define WCD937X_FLYBACK_VNEGDAC_CTRL_2 0x30AF +#define WCD937X_FLYBACK_VNEGDAC_CTRL_3 0x30B0 +#define WCD937X_FLYBACK_CTRL_1 0x30B1 +#define WCD937X_FLYBACK_TEST_CTL 0x30B2 +#define WCD937X_RX_AUX_SW_CTL 0x30B3 +#define WCD937X_RX_PA_AUX_IN_CONN 0x30B4 +#define WCD937X_RX_TIMER_DIV 0x30B5 +#define WCD937X_RX_OCP_CTL 0x30B6 +#define WCD937X_RX_OCP_COUNT 0x30B7 +#define WCD937X_RX_BIAS_EAR_DAC 0x30B8 +#define WCD937X_RX_BIAS_EAR_AMP 0x30B9 +#define WCD937X_RX_BIAS_HPH_LDO 0x30BA +#define WCD937X_RX_BIAS_HPH_PA 0x30BB +#define WCD937X_RX_BIAS_HPH_RDACBUFF_CNP2 0x30BC +#define WCD937X_RX_BIAS_HPH_RDAC_LDO 0x30BD +#define WCD937X_RX_BIAS_HPH_CNP1 0x30BE +#define WCD937X_RX_BIAS_HPH_LOWPOWER 0x30BF +#define WCD937X_RX_BIAS_AUX_DAC 0x30C0 +#define WCD937X_RX_BIAS_AUX_AMP 0x30C1 +#define WCD937X_RX_BIAS_VNEGDAC_BLEEDER 0x30C2 +#define WCD937X_RX_BIAS_MISC 0x30C3 +#define WCD937X_RX_BIAS_BUCK_RST 0x30C4 +#define WCD937X_RX_BIAS_BUCK_VREF_ERRAMP 0x30C5 +#define WCD937X_RX_BIAS_FLYB_ERRAMP 0x30C6 +#define WCD937X_RX_BIAS_FLYB_BUFF 0x30C7 +#define WCD937X_RX_BIAS_FLYB_MID_RST 0x30C8 +#define WCD937X_HPH_L_STATUS 0x30C9 +#define WCD937X_HPH_R_STATUS 0x30CA +#define WCD937X_HPH_CNP_EN 0x30CB +#define WCD937X_HPH_CNP_WG_CTL 0x30CC +#define WCD937X_HPH_CNP_WG_TIME 0x30CD +#define WCD937X_HPH_OCP_CTL 0x30CE +#define WCD937X_HPH_AUTO_CHOP 0x30CF +#define WCD937X_HPH_CHOP_CTL 0x30D0 +#define WCD937X_HPH_PA_CTL1 0x30D1 +#define WCD937X_HPH_PA_CTL2 0x30D2 +#define WCD937X_HPHPA_GND_R_MASK BIT(6) +#define WCD937X_HPHPA_GND_L_MASK BIT(4) +#define WCD937X_HPH_L_EN 0x30D3 +#define WCD937X_HPH_L_TEST 0x30D4 +#define WCD937X_HPH_L_ATEST 0x30D5 +#define WCD937X_HPH_R_EN 0x30D6 +#define WCD937X_GAIN_SRC_SEL_MASK BIT(5) +#define WCD937X_GAIN_SRC_SEL_REGISTER 1 +#define WCD937X_HPH_R_TEST 0x30D7 +#define WCD937X_HPH_R_ATEST 0x30D8 +#define WCD937X_HPH_RDAC_CLK_CTL1 0x30D9 +#define WCD937X_HPHPA_GND_OVR_MASK BIT(1) +#define WCD937X_CHOP_CLK_EN_MASK BIT(7) +#define WCD937X_HPH_RDAC_CLK_CTL2 0x30DA +#define WCD937X_HPH_RDAC_LDO_CTL 0x30DB +#define WCD937X_HPH_RDAC_CHOP_CLK_LP_CTL 0x30DC +#define WCD937X_HPH_REFBUFF_UHQA_CTL 0x30DD +#define WCD937X_HPH_REFBUFF_LP_CTL 0x30DE +#define WCD937X_PREREF_FLIT_BYPASS_MASK BIT(0) +#define WCD937X_HPH_L_DAC_CTL 0x30DF +#define WCD937X_HPH_R_DAC_CTL 0x30E0 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_COMP_SEL 0x30E1 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_EN 0x30E2 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_MISC1 0x30E3 +#define WCD937X_HPH_SURGE_HPHLR_SURGE_STATUS 0x30E4 +#define WCD937X_EAR_EAR_EN_REG 0x30E9 +#define WCD937X_EAR_EAR_PA_CON 0x30EA +#define WCD937X_EAR_EAR_SP_CON 0x30EB +#define WCD937X_EAR_EAR_DAC_CON 0x30EC +#define WCD937X_EAR_EAR_CNP_FSM_CON 0x30ED +#define WCD937X_EAR_TEST_CTL 0x30EE +#define WCD937X_EAR_STATUS_REG_1 0x30EF +#define WCD937X_EAR_STATUS_REG_2 0x30F0 +#define WCD937X_ANA_NEW_PAGE_REGISTER 0x3100 +#define WCD937X_HPH_NEW_ANA_HPH2 0x3101 +#define WCD937X_HPH_NEW_ANA_HPH3 0x3102 +#define WCD937X_SLEEP_CTL 0x3103 +#define WCD937X_SLEEP_WATCHDOG_CTL 0x3104 +#define WCD937X_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x311F +#define WCD937X_MBHC_NEW_CTL_1 0x3120 +#define WCD937X_MBHC_CTL_RCO_EN_MASK BIT(7) +#define WCD937X_MBHC_CTL_RCO_EN BIT(7) +#define WCD937X_MBHC_BTN_DBNC_MASK GENMASK(1, 0) +#define WCD937X_MBHC_BTN_DBNC_T_16_MS 0x2 +#define WCD937X_MBHC_NEW_CTL_2 0x3121 +#define WCD937X_MBHC_NEW_PLUG_DETECT_CTL 0x3122 +#define WCD937X_MBHC_NEW_ZDET_ANA_CTL 0x3123 +#define WCD937X_M_RTH_CTL_MASK GENMASK(3, 2) +#define WCD937X_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0) +#define WCD937X_MBHC_HS_VREF_1P5_V 0x1 +#define WCD937X_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6 +#define WCD937X_ZDET_RANGE_CTL_MASK GENMASK(3, 0) +#define WCD937X_ZDET_MAXV_CTL_MASK GENMASK(6, 4) +#define WCD937X_MBHC_NEW_ZDET_RAMP_CTL 0x3124 +#define WCD937X_MBHC_NEW_FSM_STATUS 0x3125 +#define WCD937X_MBHC_NEW_ADC_RESULT 0x3126 +#define WCD937X_TX_NEW_TX_CH2_SEL 0x3127 +#define WCD937X_AUX_AUXPA 0x3128 +#define WCD937X_AUXPA_CLK_EN_MASK BIT(4) +#define WCD937X_AUXPA_CLK_EN_MASK BIT(4) +#define WCD937X_LDORXTX_MODE 0x3129 +#define WCD937X_LDORXTX_CONFIG 0x312A +#define WCD937X_DIE_CRACK_DIE_CRK_DET_EN 0x312C +#define WCD937X_DIE_CRACK_DIE_CRK_DET_OUT 0x312D +#define WCD937X_HPH_NEW_INT_RDAC_GAIN_CTL 0x3132 +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x3133 +#define WCD937X_HPH_NEW_INT_RDAC_VREF_CTL 0x3134 +#define WCD937X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x3135 +#define WCD937X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x3136 +#define WCD937X_HPH_NEW_INT_PA_MISC1 0x3137 +#define WCD937X_HPH_NEW_INT_PA_MISC2 0x3138 +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC 0x3139 +#define WCD937X_HPH_NEW_INT_HPH_TIMER1 0x313A +#define WCD937X_HPH_NEW_INT_HPH_TIMER2 0x313B +#define WCD937X_HPH_NEW_INT_HPH_TIMER3 0x313C +#define WCD937X_HPH_NEW_INT_HPH_TIMER4 0x313D +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC2 0x313E +#define WCD937X_HPH_NEW_INT_PA_RDAC_MISC3 0x313F +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x3145 +#define WCD937X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x3146 +#define WCD937X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x3147 +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL 0x31AF +#define WCD937X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL 0x31B0 +#define WCD937X_MOISTURE_EN_POLLING_MASK BIT(2) +#define WCD937X_HSDET_PULLUP_C_MASK GENMASK(4, 0) +#define WCD937X_MBHC_NEW_INT_MECH_DET_CURRENT 0x31B1 +#define WCD937X_MBHC_NEW_INT_SPARE_2 0x31B2 +#define WCD937X_EAR_INT_NEW_EAR_CHOPPER_CON 0x31B7 +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON1 0x31B8 +#define WCD937X_EAR_INT_NEW_CNP_VCM_CON2 0x31B9 +#define WCD937X_EAR_INT_NEW_EAR_DYNAMIC_BIAS 0x31BA +#define WCD937X_AUX_INT_EN_REG 0x31BD +#define WCD937X_AUX_INT_PA_CTRL 0x31BE +#define WCD937X_AUX_INT_SP_CTRL 0x31BF +#define WCD937X_AUX_INT_DAC_CTRL 0x31C0 +#define WCD937X_AUX_INT_CLK_CTRL 0x31C1 +#define WCD937X_AUX_INT_TEST_CTRL 0x31C2 +#define WCD937X_AUX_INT_STATUS_REG 0x31C3 +#define WCD937X_AUX_INT_MISC 0x31C4 +#define WCD937X_LDORXTX_INT_BIAS 0x31C5 +#define WCD937X_LDORXTX_INT_STB_LOADS_DTEST 0x31C6 +#define WCD937X_LDORXTX_INT_TEST0 0x31C7 +#define WCD937X_LDORXTX_INT_STARTUP_TIMER 0x31C8 +#define WCD937X_LDORXTX_INT_TEST1 0x31C9 +#define WCD937X_LDORXTX_INT_STATUS 0x31CA +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_1 0x31D0 +#define WCD937X_SLEEP_INT_WATCHDOG_CTL_2 0x31D1 +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT1 0x31D3 +#define WCD937X_DIE_CRACK_INT_DIE_CRK_DET_INT2 0x31D4 +#define WCD937X_DIGITAL_PAGE_REGISTER 0x3400 +#define WCD937X_DIGITAL_CHIP_ID0 0x3401 +#define WCD937X_DIGITAL_CHIP_ID1 0x3402 +#define WCD937X_DIGITAL_CHIP_ID2 0x3403 +#define WCD937X_DIGITAL_CHIP_ID3 0x3404 +#define WCD937X_DIGITAL_CDC_RST_CTL 0x3406 +#define WCD937X_DIGITAL_TOP_CLK_CFG 0x3407 +#define WCD937X_DIGITAL_CDC_ANA_CLK_CTL 0x3408 +#define WCD937X_DIGITAL_CDC_DIG_CLK_CTL 0x3409 +#define WCD937X_DIGITAL_SWR_RST_EN 0x340A +#define WCD937X_DIGITAL_CDC_PATH_MODE 0x340B +#define WCD937X_DIGITAL_CDC_RX_RST 0x340C +#define WCD937X_DIGITAL_CDC_RX0_CTL 0x340D +#define WCD937X_DIGITAL_CDC_RX1_CTL 0x340E +#define WCD937X_DIGITAL_CDC_RX2_CTL 0x340F +#define WCD937X_DIGITAL_DEM_BYPASS_DATA0 0x3410 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA1 0x3411 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA2 0x3412 +#define WCD937X_DIGITAL_DEM_BYPASS_DATA3 0x3413 +#define WCD937X_DIGITAL_CDC_COMP_CTL_0 0x3414 +#define WCD937X_DIGITAL_CDC_RX_DELAY_CTL 0x3417 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_0 0x3418 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A1_1 0x3419 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_0 0x341A +#define WCD937X_DIGITAL_CDC_HPH_DSM_A2_1 0x341B +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_0 0x341C +#define WCD937X_DIGITAL_CDC_HPH_DSM_A3_1 0x341D +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_0 0x341E +#define WCD937X_DIGITAL_CDC_HPH_DSM_A4_1 0x341F +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_0 0x3420 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A5_1 0x3421 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A6_0 0x3422 +#define WCD937X_DIGITAL_CDC_HPH_DSM_A7_0 0x3423 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_0 0x3424 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_1 0x3425 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_2 0x3426 +#define WCD937X_DIGITAL_CDC_HPH_DSM_C_3 0x3427 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R1 0x3428 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R2 0x3429 +#define WCD937X_DIGITAL_CDC_HPH_DSM_R3 0x342A +#define WCD937X_DIGITAL_CDC_HPH_DSM_R4 0x342B +#define WCD937X_DIGITAL_CDC_HPH_DSM_R5 0x342C +#define WCD937X_DIGITAL_CDC_HPH_DSM_R6 0x342D +#define WCD937X_DIGITAL_CDC_HPH_DSM_R7 0x342E +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_0 0x342F +#define WCD937X_DIGITAL_CDC_AUX_DSM_A1_1 0x3430 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_0 0x3431 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A2_1 0x3432 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_0 0x3433 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A3_1 0x3434 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_0 0x3435 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A4_1 0x3436 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_0 0x3437 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A5_1 0x3438 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A6_0 0x3439 +#define WCD937X_DIGITAL_CDC_AUX_DSM_A7_0 0x343A +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_0 0x343B +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_1 0x343C +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_2 0x343D +#define WCD937X_DIGITAL_CDC_AUX_DSM_C_3 0x343E +#define WCD937X_DIGITAL_CDC_AUX_DSM_R1 0x343F +#define WCD937X_DIGITAL_CDC_AUX_DSM_R2 0x3440 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R3 0x3441 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R4 0x3442 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R5 0x3443 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R6 0x3444 +#define WCD937X_DIGITAL_CDC_AUX_DSM_R7 0x3445 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_0 0x3446 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_RX_1 0x3447 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_0 0x3448 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_1 0x3449 +#define WCD937X_DIGITAL_CDC_HPH_GAIN_DSD_2 0x344A +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_0 0x344B +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_1 0x344C +#define WCD937X_DIGITAL_CDC_AUX_GAIN_DSD_2 0x344D +#define WCD937X_DIGITAL_CDC_HPH_GAIN_CTL 0x344E +#define WCD937X_DIGITAL_CDC_AUX_GAIN_CTL 0x344F +#define WCD937X_DIGITAL_CDC_EAR_PATH_CTL 0x3450 +#define WCD937X_DIGITAL_CDC_SWR_CLH 0x3451 +#define WCD937X_DIGITAL_SWR_CLH_BYP 0x3452 +#define WCD937X_DIGITAL_CDC_TX0_CTL 0x3453 +#define WCD937X_DIGITAL_CDC_TX1_CTL 0x3454 +#define WCD937X_DIGITAL_CDC_TX2_CTL 0x3455 +#define WCD937X_DIGITAL_CDC_TX_RST 0x3456 +#define WCD937X_DIGITAL_CDC_REQ_CTL 0x3457 +#define WCD937X_DIGITAL_CDC_AMIC_CTL 0x345A +#define WCD937X_DIGITAL_CDC_DMIC_CTL 0x345B +#define WCD937X_DIGITAL_CDC_DMIC1_CTL 0x345C +#define WCD937X_DIGITAL_CDC_DMIC2_CTL 0x345D +#define WCD937X_DIGITAL_CDC_DMIC3_CTL 0x345E +#define WCD937X_DIGITAL_EFUSE_CTL 0x345F +#define WCD937X_DIGITAL_EFUSE_PRG_CTL 0x3460 +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_0 0x3461 +#define WCD937X_DIGITAL_EFUSE_TEST_CTL_1 0x3462 +#define WCD937X_DIGITAL_EFUSE_T_DATA_0 0x3463 +#define WCD937X_DIGITAL_EFUSE_T_DATA_1 0x3464 +#define WCD937X_DIGITAL_PDM_WD_CTL0 0x3465 +#define WCD937X_DIGITAL_PDM_WD_CTL1 0x3466 +#define WCD937X_DIGITAL_PDM_WD_CTL2 0x3467 +#define WCD937X_DIGITAL_INTR_MODE 0x346A +#define WCD937X_DIGITAL_INTR_MASK_0 0x346B +#define WCD937X_DIGITAL_INTR_MASK_1 0x346C +#define WCD937X_DIGITAL_INTR_MASK_2 0x346D +#define WCD937X_DIGITAL_INTR_STATUS_0 0x346E +#define WCD937X_DIGITAL_INTR_STATUS_1 0x346F +#define WCD937X_DIGITAL_INTR_STATUS_2 0x3470 +#define WCD937X_DIGITAL_INTR_CLEAR_0 0x3471 +#define WCD937X_DIGITAL_INTR_CLEAR_1 0x3472 +#define WCD937X_DIGITAL_INTR_CLEAR_2 0x3473 +#define WCD937X_DIGITAL_INTR_LEVEL_0 0x3474 +#define WCD937X_DIGITAL_INTR_LEVEL_1 0x3475 +#define WCD937X_DIGITAL_INTR_LEVEL_2 0x3476 +#define WCD937X_DIGITAL_INTR_SET_0 0x3477 +#define WCD937X_DIGITAL_INTR_SET_1 0x3478 +#define WCD937X_DIGITAL_INTR_SET_2 0x3479 +#define WCD937X_DIGITAL_INTR_TEST_0 0x347A +#define WCD937X_DIGITAL_INTR_TEST_1 0x347B +#define WCD937X_DIGITAL_INTR_TEST_2 0x347C +#define WCD937X_DIGITAL_CDC_CONN_RX0_CTL 0x347F +#define WCD937X_DIGITAL_CDC_CONN_RX1_CTL 0x3480 +#define WCD937X_DIGITAL_CDC_CONN_RX2_CTL 0x3481 +#define WCD937X_DIGITAL_CDC_CONN_TX_CTL 0x3482 +#define WCD937X_DIGITAL_LOOP_BACK_MODE 0x3483 +#define WCD937X_DIGITAL_SWR_DAC_TEST 0x3484 +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_0 0x3485 +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_0 0x3491 +#define WCD937X_DIGITAL_SWR_HM_TEST_RX_1 0x3492 +#define WCD937X_DIGITAL_SWR_HM_TEST_TX_1 0x3493 +#define WCD937X_DIGITAL_SWR_HM_TEST 0x3494 +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX0 0x3495 +#define WCD937X_DIGITAL_PAD_CTL_PDM_RX1 0x3496 +#define WCD937X_DIGITAL_PAD_CTL_PDM_TX0 0x3497 +#define WCD937X_DIGITAL_PAD_CTL_PDM_TX1 0x3498 +#define WCD937X_DIGITAL_PAD_INP_DIS_0 0x3499 +#define WCD937X_DIGITAL_PAD_INP_DIS_1 0x349A +#define WCD937X_DIGITAL_DRIVE_STRENGTH_0 0x349B +#define WCD937X_DIGITAL_DRIVE_STRENGTH_1 0x349C +#define WCD937X_DIGITAL_DRIVE_STRENGTH_2 0x349D +#define WCD937X_DIGITAL_RX_DATA_EDGE_CTL 0x349E +#define WCD937X_DIGITAL_TX_DATA_EDGE_CTL 0x349F +#define WCD937X_DIGITAL_GPIO_MODE 0x34A0 +#define WCD937X_DIGITAL_PIN_CTL_OE 0x34A1 +#define WCD937X_DIGITAL_PIN_CTL_DATA_0 0x34A2 +#define WCD937X_DIGITAL_PIN_CTL_DATA_1 0x34A3 +#define WCD937X_DIGITAL_PIN_STATUS_0 0x34A4 +#define WCD937X_DIGITAL_PIN_STATUS_1 0x34A5 +#define WCD937X_DIGITAL_DIG_DEBUG_CTL 0x34A6 +#define WCD937X_DIGITAL_DIG_DEBUG_EN 0x34A7 +#define WCD937X_DIGITAL_ANA_CSR_DBG_ADD 0x34A8 +#define WCD937X_DIGITAL_ANA_CSR_DBG_CTL 0x34A9 +#define WCD937X_DIGITAL_SSP_DBG 0x34AA +#define WCD937X_DIGITAL_MODE_STATUS_0 0x34AB +#define WCD937X_DIGITAL_MODE_STATUS_1 0x34AC +#define WCD937X_DIGITAL_SPARE_0 0x34AD +#define WCD937X_DIGITAL_SPARE_1 0x34AE +#define WCD937X_DIGITAL_SPARE_2 0x34AF +#define WCD937X_DIGITAL_EFUSE_REG_0 0x34B0 +#define WCD937X_DIGITAL_EFUSE_REG_1 0x34B1 +#define WCD937X_DIGITAL_EFUSE_REG_2 0x34B2 +#define WCD937X_DIGITAL_EFUSE_REG_3 0x34B3 +#define WCD937X_DIGITAL_EFUSE_REG_4 0x34B4 +#define WCD937X_DIGITAL_EFUSE_REG_5 0x34B5 +#define WCD937X_DIGITAL_EFUSE_REG_6 0x34B6 +#define WCD937X_DIGITAL_EFUSE_REG_7 0x34B7 +#define WCD937X_DIGITAL_EFUSE_REG_8 0x34B8 +#define WCD937X_DIGITAL_EFUSE_REG_9 0x34B9 +#define WCD937X_DIGITAL_EFUSE_REG_10 0x34BA +#define WCD937X_DIGITAL_EFUSE_REG_11 0x34BB +#define WCD937X_DIGITAL_EFUSE_REG_12 0x34BC +#define WCD937X_DIGITAL_EFUSE_REG_13 0x34BD +#define WCD937X_DIGITAL_EFUSE_REG_14 0x34BE +#define WCD937X_DIGITAL_EFUSE_REG_15 0x34BF +#define WCD937X_DIGITAL_EFUSE_REG_16 0x34C0 +#define WCD937X_DIGITAL_EFUSE_REG_17 0x34C1 +#define WCD937X_DIGITAL_EFUSE_REG_18 0x34C2 +#define WCD937X_DIGITAL_EFUSE_REG_19 0x34C3 +#define WCD937X_DIGITAL_EFUSE_REG_20 0x34C4 +#define WCD937X_DIGITAL_EFUSE_REG_21 0x34C5 +#define WCD937X_DIGITAL_EFUSE_REG_22 0x34C6 +#define WCD937X_DIGITAL_EFUSE_REG_23 0x34C7 +#define WCD937X_DIGITAL_EFUSE_REG_24 0x34C8 +#define WCD937X_DIGITAL_EFUSE_REG_25 0x34C9 +#define WCD937X_DIGITAL_EFUSE_REG_26 0x34CA +#define WCD937X_DIGITAL_EFUSE_REG_27 0x34CB +#define WCD937X_DIGITAL_EFUSE_REG_28 0x34CC +#define WCD937X_DIGITAL_EFUSE_REG_29 0x34CD +#define WCD937X_DIGITAL_EFUSE_REG_30 0x34CE +#define WCD937X_DIGITAL_EFUSE_REG_31 0x34CF +#define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31) + +#define WCD937X_MAX_MICBIAS 3 +#define WCD937X_MAX_BULK_SUPPLY 4 +#define WCD937X_MAX_TX_SWR_PORTS 4 +#define WCD937X_MAX_SWR_PORTS 5 +#define WCD937X_MAX_SWR_CH_IDS 15 + +struct wcd937x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +struct wcd937x_priv; +struct wcd937x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; + const struct wcd937x_sdw_ch_info *ch_info; + bool port_enable[WCD937X_MAX_SWR_CH_IDS]; + int active_ports; + bool is_tx; + struct wcd937x_priv *wcd937x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD937X_SDW) +int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd937x_sdw_device_get(struct device_node *np); + +#else +int wcd937x_sdw_free(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +int wcd937x_sdw_set_sdw_stream(struct wcd937x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} +#endif + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD937X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD937X_IRQ_MBHC_SW_DET, + WCD937X_IRQ_HPHR_OCP_INT, + WCD937X_IRQ_HPHR_CNP_INT, + WCD937X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD937X_IRQ_HPHL_CNP_INT, + WCD937X_IRQ_EAR_CNP_INT, + WCD937X_IRQ_EAR_SCD_INT, + WCD937X_IRQ_AUX_CNP_INT, + WCD937X_IRQ_AUX_SCD_INT, + WCD937X_IRQ_HPHL_PDM_WD_INT, + WCD937X_IRQ_HPHR_PDM_WD_INT, + WCD937X_IRQ_AUX_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD937X_IRQ_LDORT_SCD_INT, + WCD937X_IRQ_MBHC_MOISTURE_INT, + WCD937X_IRQ_HPHL_SURGE_DET_INT, + WCD937X_IRQ_HPHR_SURGE_DET_INT, + WCD937X_NUM_IRQS, +}; + +enum wcd937x_tx_sdw_ports { + WCD937X_ADC_1_PORT = 1, + WCD937X_ADC_2_3_PORT, + WCD937X_DMIC_0_3_MBHC_PORT, + WCD937X_DMIC_4_6_PORT, +}; + +enum wcd937x_tx_sdw_channels { + WCD937X_ADC1, + WCD937X_ADC2, + WCD937X_ADC3, + WCD937X_DMIC0, + WCD937X_DMIC1, + WCD937X_MBHC, + WCD937X_DMIC2, + WCD937X_DMIC3, + WCD937X_DMIC4, + WCD937X_DMIC5, + WCD937X_DMIC6, +}; + +enum wcd937x_rx_sdw_ports { + WCD937X_HPH_PORT = 1, + WCD937X_CLSH_PORT, + WCD937X_COMP_PORT, + WCD937X_LO_PORT, + WCD937X_DSD_PORT, +}; + +enum wcd937x_rx_sdw_channels { + WCD937X_HPH_L, + WCD937X_HPH_R, + WCD937X_CLSH, + WCD937X_COMP_L, + WCD937X_COMP_R, + WCD937X_LO, + WCD937X_DSD_R, + WCD937X_DSD_L, +}; + +#endif diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index a1f04010da95..c995bcc59ead 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -21,7 +21,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)), @@ -32,7 +32,7 @@ static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)), }; -static struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)), WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)), WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)), diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 6021aa5a5689..12b32d5dc580 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -206,7 +206,6 @@ struct wcd938x_priv { bool comp1_enable; bool comp2_enable; bool ldoh; - bool bcs_dis; }; static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); @@ -222,7 +221,7 @@ struct wcd938x_mbhc_zdet_param { u16 btn7; }; -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20), @@ -419,7 +418,7 @@ static int wcd938x_io_init(struct wcd938x_priv *wcd938x) } -static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, +static int wcd938x_sdw_connect_port(const struct wcd938x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { @@ -1650,31 +1649,6 @@ static int wcd938x_ldoh_put(struct snd_kcontrol *kcontrol, return 1; } -static int wcd938x_bcs_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - - ucontrol->value.integer.value[0] = wcd938x->bcs_dis; - - return 0; -} - -static int wcd938x_bcs_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - - if (wcd938x->bcs_dis == ucontrol->value.integer.value[0]) - return 0; - - wcd938x->bcs_dis = ucontrol->value.integer.value[0]; - - return 1; -} - static const char * const tx_mode_mux_text_wcd9380[] = { "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", }; @@ -1982,7 +1956,7 @@ static bool wcd938x_mbhc_micb_en_status(struct snd_soc_component *component, int if (micb_num == MIC_BIAS_2) { val = snd_soc_component_read_field(component, WCD938X_ANA_MICB2, - WCD938X_ANA_MICB2_ENABLE_MASK); + WCD938X_MICB_EN_MASK); if (val == WCD938X_MICB_ENABLE) return true; } @@ -2695,8 +2669,6 @@ static const struct snd_kcontrol_new wcd938x_snd_controls[] = { wcd938x_get_swr_port, wcd938x_set_swr_port), SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, wcd938x_ldoh_get, wcd938x_ldoh_put), - SOC_SINGLE_EXT("ADC2_BCS Disable Switch", SND_SOC_NOPM, 0, 1, 0, - wcd938x_bcs_get, wcd938x_bcs_put), SOC_SINGLE_TLV("ADC1 Volume", WCD938X_ANA_TX_CH1, 0, 20, 0, analog_gain), SOC_SINGLE_TLV("ADC2 Volume", WCD938X_ANA_TX_CH2, 0, 20, 0, analog_gain), @@ -3055,7 +3027,7 @@ static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD938x", }; diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 74b1498fec38..b2ad98026ae2 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -75,9 +75,6 @@ #define WCD938X_MICB_PULL_UP 2 #define WCD938X_MICB_PULL_DOWN 3 #define WCD938X_ANA_MICB2 (0x3023) -#define WCD938X_ANA_MICB2_ENABLE BIT(6) -#define WCD938X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) -#define WCD938X_ANA_MICB2_VOUT_MASK GENMASK(5, 0) #define WCD938X_ANA_MICB2_RAMP (0x3024) #define WCD938X_RAMP_EN_MASK BIT(7) #define WCD938X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) @@ -645,10 +642,6 @@ enum wcd938x_rx_sdw_channels { WCD938X_DSD_R, WCD938X_DSD_L, }; -enum { - WCD938X_SDW_DIR_RX, - WCD938X_SDW_DIR_TX, -}; struct wcd938x_priv; struct wcd938x_sdw_priv { @@ -656,10 +649,9 @@ struct wcd938x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; - struct wcd938x_sdw_ch_info *ch_info; + const struct wcd938x_sdw_ch_info *ch_info; bool port_enable[WCD938X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd938x_priv *wcd938x; struct irq_domain *slave_irq; diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index 8acb5651c5bc..94b1e99a3ca0 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -23,7 +23,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { +static const 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)), @@ -36,7 +36,7 @@ static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), }; -static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { +static const 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)), diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index c49894aad8a5..68fc591670dc 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -85,7 +85,6 @@ enum { #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 { @@ -182,8 +181,6 @@ struct wcd939x_priv { /* 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; @@ -221,7 +218,7 @@ 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] = { +static const 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), @@ -292,7 +289,7 @@ static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), }; -static struct regmap_irq_chip wcd939x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd939x_regmap_irq_chip = { .name = "wcd939x", .irqs = wcd939x_irqs, .num_irqs = ARRAY_SIZE(wcd939x_irqs), @@ -415,7 +412,7 @@ static int wcd939x_io_init(struct snd_soc_component *component) return 0; } -static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, +static int wcd939x_sdw_connect_port(const struct wcd939x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { @@ -525,7 +522,7 @@ static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, WCD939X_DIGITAL_CDC_COMP_CTL_0, WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, true); - /* 5msec compander delay as per HW requirement */ + /* 5msec compander delay as per HW requirement */ if (!wcd939x->comp2_enable || snd_soc_component_read_field(component, WCD939X_DIGITAL_CDC_COMP_CTL_0, @@ -1268,25 +1265,20 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, { 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", @@ -1300,7 +1292,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, 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); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_UP); break; case MICB_PULLUP_DISABLE: if (wcd939x->pullup_ref[micb_index] > 0) @@ -1308,7 +1301,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, 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); + WCD939X_MICB_ENABLE, + MICB_BIAS_DISABLE); break; case MICB_ENABLE: wcd939x->micb_ref[micb_index]++; @@ -1345,7 +1339,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, 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, + snd_soc_component_write_field(component, micb_reg, + WCD939X_MICB_ENABLE, MICB_BIAS_ENABLE); if (micb_num == MIC_BIAS_2) wcd_mbhc_event_notify(wcd939x->wcd_mbhc, @@ -1362,7 +1357,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, 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); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_UP); else if (wcd939x->micb_ref[micb_index] == 0 && wcd939x->pullup_ref[micb_index] == 0) { if (micb_num == MIC_BIAS_2) @@ -1370,7 +1366,8 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, WCD_EVENT_PRE_MICBIAS_2_OFF); snd_soc_component_write_field(component, micb_reg, - micb_field, MICB_BIAS_DISABLE); + WCD939X_MICB_ENABLE, + MICB_BIAS_DISABLE); if (micb_num == MIC_BIAS_2) wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_POST_MICBIAS_2_OFF); @@ -1869,11 +1866,10 @@ static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component, 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, + val = FIELD_GET(WCD939X_MICB_ENABLE, snd_soc_component_read(component, WCD939X_ANA_MICB2)); if (val == MICB_BIAS_ENABLE) return true; @@ -1935,7 +1931,6 @@ 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; @@ -1943,23 +1938,15 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, 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; @@ -1975,9 +1962,9 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, * micbias. */ micb_en = snd_soc_component_read_field(component, micb_reg, - micb_en_field); + WCD939X_MICB_ENABLE); cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, - micb_vout_ctl_field); + WCD939X_MICB_VOUT_CTL); req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt); if (req_vout_ctl < 0) { @@ -1996,14 +1983,16 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, if (micb_en == MICB_BIAS_ENABLE) snd_soc_component_write_field(component, micb_reg, - micb_en_field, MICB_BIAS_PULL_DOWN); + WCD939X_MICB_ENABLE, + MICB_BIAS_PULL_DOWN); snd_soc_component_write_field(component, micb_reg, - micb_vout_ctl_field, req_vout_ctl); + WCD939X_MICB_VOUT_CTL, req_vout_ctl); if (micb_en == MICB_BIAS_ENABLE) { snd_soc_component_write_field(component, micb_reg, - micb_en_field, MICB_BIAS_ENABLE); + WCD939X_MICB_ENABLE, + MICB_BIAS_ENABLE); /* * Add 2ms delay as per HW requirement after enabling * micbias @@ -2916,13 +2905,13 @@ static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x) return -EINVAL; regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1, - WCD939X_MICB1_VOUT_CTL, vout_ctl_1); + WCD939X_MICB_VOUT_CTL, vout_ctl_1); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2, - WCD939X_MICB2_VOUT_CTL, vout_ctl_2); + WCD939X_MICB_VOUT_CTL, vout_ctl_2); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3, - WCD939X_MICB3_VOUT_CTL, vout_ctl_3); + WCD939X_MICB_VOUT_CTL, vout_ctl_3); regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4, - WCD939X_MICB4_VOUT_CTL, vout_ctl_4); + WCD939X_MICB_VOUT_CTL, vout_ctl_4); return 0; } @@ -2966,7 +2955,7 @@ static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) * \- regmap_irq_thread() * \- handle_nested_irq(i) */ -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD939x", }; @@ -3528,6 +3517,68 @@ static const struct component_master_ops wcd939x_comp_ops = { .unbind = wcd939x_unbind, }; +static void __maybe_unused wcd939x_typec_mux_unregister(void *data) +{ + struct typec_mux_dev *typec_mux = data; + + typec_mux_unregister(typec_mux); +} + +static void __maybe_unused wcd939x_typec_switch_unregister(void *data) +{ + struct typec_switch_dev *typec_sw = data; + + typec_switch_unregister(typec_sw); +} + +static int wcd939x_add_typec(struct wcd939x_priv *wcd939x, struct device *dev) +{ +#if IS_ENABLED(CONFIG_TYPEC) + int ret; + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; + 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, + }; + + /* + * Is USBSS is used to mux analog lines, + * register a typec mux/switch to get typec events + */ + if (!wcd939x->typec_analog_mux) + return 0; + + typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(typec_mux)) + return dev_err_probe(dev, PTR_ERR(typec_mux), + "failed to register typec mux\n"); + + ret = devm_add_action_or_reset(dev, wcd939x_typec_mux_unregister, + typec_mux); + if (ret) + return ret; + + typec_sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(typec_sw)) + return dev_err_probe(dev, PTR_ERR(typec_sw), + "failed to register typec switch\n"); + + ret = devm_add_action_or_reset(dev, wcd939x_typec_switch_unregister, + typec_sw); + if (ret) + return ret; +#endif + + return 0; +} + static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, struct device *dev, struct component_match **matchptr) @@ -3576,42 +3627,13 @@ static int wcd939x_probe(struct platform_device *pdev) 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_typec(wcd939x, dev); + if (ret) + goto err_disable_regulators; ret = wcd939x_add_slave_components(wcd939x, dev, &match); if (ret) - goto err_unregister_typec_switch; + goto err_disable_regulators; wcd939x_reset(wcd939x); @@ -3628,18 +3650,6 @@ static int wcd939x_probe(struct platform_device *pdev) 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); diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 807cf3113d20..1571c2120cfc 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -91,11 +91,9 @@ #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_MICB_ENABLE GENMASK(7, 6) +#define WCD939X_MICB_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) @@ -103,11 +101,7 @@ #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) @@ -909,21 +903,15 @@ enum wcd939x_rx_sdw_channels { 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; + const 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; diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 8f862729a2ca..edd2cb185c42 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -115,14 +115,6 @@ struct wm0010_priv { struct completion boot_completion; }; -struct wm0010_spi_msg { - struct spi_message m; - struct spi_transfer t; - u8 *tx_buf; - u8 *rx_buf; - size_t len; -}; - static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0), }; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 68d2d6444533..9f8549b34e30 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -601,7 +601,7 @@ static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) return -EINVAL; } - switch (cs_dsp->fw_ver) { + switch (cs_dsp->wmfw_ver) { case 0: case 1: ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 1253695bebd8..0478599d0f35 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -634,7 +634,7 @@ static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) } } -static struct regmap_config wsa881x_regmap_config = { +static const struct regmap_config wsa881x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index a2e86ef7d18f..d0ab4e2290b6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -9,7 +9,6 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/printk.h> #include <linux/regmap.h> @@ -935,7 +934,7 @@ static bool wsa883x_volatile_register(struct device *dev, unsigned int reg) return wsa883x_readonly_register(dev, reg); } -static struct regmap_config wsa883x_regmap_config = { +static const struct regmap_config wsa883x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, @@ -1399,6 +1398,14 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->sconfig.direction = SDW_DATA_DIR_RX; wsa883x->sconfig.type = SDW_STREAM_PDM; + /** + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1], + WSA883X_MAX_SWR_PORTS)) + dev_dbg(dev, "Static Port mapping not specified\n"); + pdev->prop.sink_ports = GENMASK(WSA883X_MAX_SWR_PORTS, 0); pdev->prop.simple_clk_stop_capable = true; pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index a9767ef0e39d..d17ae17b2938 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1319,7 +1319,7 @@ static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) return wsa884x_readonly_register(dev, reg); } -static struct regmap_config wsa884x_regmap_config = { +static const struct regmap_config wsa884x_regmap_config = { .reg_bits = 32, .val_bits = 8, .cache_type = REGCACHE_MAPLE, @@ -1887,6 +1887,14 @@ static int wsa884x_probe(struct sdw_slave *pdev, wsa884x->sconfig.direction = SDW_DATA_DIR_RX; wsa884x->sconfig.type = SDW_STREAM_PDM; + /** + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_u32_array(dev->of_node, "qcom,port-mapping", &pdev->m_port_map[1], + WSA884X_MAX_SWR_PORTS)) + dev_dbg(dev, "Static Port mapping not specified\n"); + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); pdev->prop.simple_clk_stop_capable = true; pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 270726c134b3..e283751abfef 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -103,6 +103,7 @@ config SND_SOC_FSL_XCVR select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_FSL_UTILS help Say Y if you want to add Audio Transceiver (XCVR) support for NXP iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC, @@ -130,6 +131,13 @@ config SND_SOC_FSL_RPMSG This option is only useful for out-of-tree drivers since in-tree drivers select it automatically. +config SND_SOC_FSL_LPC3XXX + tristate "SoC Audio for NXP LPC32XX CPUs" + depends on ARCH_LPC32XX || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the LPC3XXX I2S interface. + config SND_SOC_IMX_PCM_DMA tristate select SND_SOC_GENERIC_DMAENGINE_PCM @@ -295,15 +303,6 @@ config SND_SOC_IMX_SGTL5000 SND_SOC_FSL_ASOC_CARD and SND_SOC_SGTL5000 to use the newer driver. -config SND_SOC_IMX_SPDIF - tristate "SoC Audio support for i.MX boards with S/PDIF" - select SND_SOC_IMX_PCM_DMA - select SND_SOC_FSL_SPDIF - help - SoC Audio support for i.MX boards with S/PDIF - Say Y if you want to add support for SoC audio on an i.MX board with - a S/DPDIF. - config SND_SOC_FSL_ASOC_CARD tristate "Generic ASoC Sound Card with ASRC support" depends on OF && I2C @@ -315,6 +314,7 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_ESAI select SND_SOC_FSL_SAI select SND_SOC_FSL_SSI + select SND_SOC_FSL_SPDIF select SND_SOC_TLV320AIC31XX select SND_SOC_WM8994 select MFD_WM8994 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 2fe78eed3a48..ad97244b5cc3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o snd-soc-fsl-audmix-y := fsl_audmix.o snd-soc-fsl-asoc-card-y := fsl-asoc-card.o snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o snd-soc-fsl-sai-y := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o @@ -29,6 +30,7 @@ snd-soc-fsl-qmc-audio-y := fsl_qmc_audio.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o +obj-$(CONFIG_SND_SOC_FSL_LPC3XXX) += snd-soc-fsl-lpc3xxx.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o @@ -65,7 +67,6 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o snd-soc-eukrea-tlv320-y := eukrea-tlv320.o snd-soc-imx-es8328-y := imx-es8328.o snd-soc-imx-sgtl5000-y := imx-sgtl5000.o -snd-soc-imx-spdif-y := imx-spdif.o snd-soc-imx-audmix-y := imx-audmix.o snd-soc-imx-hdmi-y := imx-hdmi.o snd-soc-imx-rpmsg-y := imx-rpmsg.o @@ -74,7 +75,6 @@ snd-soc-imx-card-y := imx-card.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o -obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index eb67689dcd6e..82df887b3af5 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -99,7 +99,7 @@ struct fsl_asoc_card_priv { struct simple_util_jack hp_jack; struct simple_util_jack mic_jack; struct platform_device *pdev; - struct codec_priv codec_priv; + struct codec_priv codec_priv[2]; struct cpu_priv cpu_priv; struct snd_soc_card card; u8 streams; @@ -172,10 +172,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - struct codec_priv *codec_priv = &priv->codec_priv; + struct codec_priv *codec_priv; + struct snd_soc_dai *codec_dai; struct cpu_priv *cpu_priv = &priv->cpu_priv; struct device *dev = rtd->card->dev; unsigned int pll_out; + int codec_idx; int ret; priv->sample_rate = params_rate(params); @@ -208,28 +210,32 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, } /* Specific configuration for PLL */ - if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { - if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) - pll_out = priv->sample_rate * 384; - else - pll_out = priv->sample_rate * 256; + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->pll_id, - codec_priv->mclk_id, - codec_priv->mclk_freq, pll_out); - if (ret) { - dev_err(dev, "failed to start FLL: %d\n", ret); - goto fail; - } + if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { + if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) + pll_out = priv->sample_rate * 384; + else + pll_out = priv->sample_rate * 256; - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->fll_id, - pll_out, SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_pll(codec_dai, + codec_priv->pll_id, + codec_priv->mclk_id, + codec_priv->mclk_freq, pll_out); + if (ret) { + dev_err(dev, "failed to start FLL: %d\n", ret); + goto fail; + } - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set SYSCLK: %d\n", ret); - goto fail; + ret = snd_soc_dai_set_sysclk(codec_dai, + codec_priv->fll_id, + pll_out, SND_SOC_CLOCK_IN); + + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set SYSCLK: %d\n", ret); + goto fail; + } } } @@ -244,28 +250,34 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct codec_priv *codec_priv = &priv->codec_priv; + struct codec_priv *codec_priv; + struct snd_soc_dai *codec_dai; struct device *dev = rtd->card->dev; + int codec_idx; int ret; priv->streams &= ~BIT(substream->stream); - if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { - /* Force freq to be free_freq to avoid error message in codec */ - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->mclk_id, - codec_priv->free_freq, - SND_SOC_CLOCK_IN); - if (ret) { - dev_err(dev, "failed to switch away from FLL: %d\n", ret); - return ret; - } + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), - codec_priv->pll_id, 0, 0, 0); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to stop FLL: %d\n", ret); - return ret; + if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { + /* Force freq to be free_freq to avoid error message in codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, + codec_priv->mclk_id, + codec_priv->free_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "failed to switch away from FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, + codec_priv->pll_id, 0, 0, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to stop FLL: %d\n", ret); + return ret; + } } } @@ -296,7 +308,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, SND_SOC_DAILINK_DEFS(hifi, DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(hifi_fe, @@ -306,7 +318,7 @@ SND_SOC_DAILINK_DEFS(hifi_fe, SND_SOC_DAILINK_DEFS(hifi_be, DAILINK_COMP_ARRAY(COMP_EMPTY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY())); static const struct snd_soc_dai_link fsl_asoc_card_dai[] = { /* Default ASoC DAI Link*/ @@ -465,6 +477,75 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, return 0; } +static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], + struct device_node *cpu_np, + const char *codec_dai_name[], + struct fsl_asoc_card_priv *priv) +{ + struct device *dev = &priv->pdev->dev; + struct device_node *np = dev->of_node; + + if (!of_node_name_eq(cpu_np, "spdif")) { + dev_err(dev, "CPU phandle invalid, should be an SPDIF device\n"); + return -EINVAL; + } + + priv->dai_link[0].playback_only = true; + priv->dai_link[0].capture_only = true; + + for (int i = 0; i < 2; i++) { + if (!codec_np[i]) + break; + + if (of_device_is_compatible(codec_np[i], "linux,spdif-dit")) { + priv->dai_link[0].capture_only = false; + codec_dai_name[i] = "dit-hifi"; + } else if (of_device_is_compatible(codec_np[i], "linux,spdif-dir")) { + priv->dai_link[0].playback_only = false; + codec_dai_name[i] = "dir-hifi"; + } + } + + // Old SPDIF DT binding + if (!codec_np[0]) { + codec_dai_name[0] = snd_soc_dummy_dlc.dai_name; + if (of_property_read_bool(np, "spdif-out")) + priv->dai_link[0].capture_only = false; + if (of_property_read_bool(np, "spdif-in")) + priv->dai_link[0].playback_only = false; + } + + if (priv->dai_link[0].playback_only && priv->dai_link[0].capture_only) { + dev_err(dev, "no enabled S/PDIF DAI link\n"); + return -EINVAL; + } + + if (priv->dai_link[0].playback_only) { + priv->dai_link[1].dpcm_capture = false; + priv->dai_link[2].dpcm_capture = false; + priv->card.dapm_routes = audio_map_tx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); + } else if (priv->dai_link[0].capture_only) { + priv->dai_link[1].dpcm_playback = false; + priv->dai_link[2].dpcm_playback = false; + priv->card.dapm_routes = audio_map_rx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); + } + + // No DAPM routes with old bindings and dummy codec + if (!codec_np[0]) { + priv->card.dapm_routes = NULL; + priv->card.num_dapm_routes = 0; + } + + if (codec_np[0] && codec_np[1]) { + priv->dai_link[0].num_codecs = 2; + priv->dai_link[2].num_codecs = 2; + } + + return 0; +} + static int hp_jack_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -504,9 +585,10 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_pcm_runtime *rtd = list_first_entry( &card->rtd_list, struct snd_soc_pcm_runtime, list); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct codec_priv *codec_priv = &priv->codec_priv; + struct snd_soc_dai *codec_dai; + struct codec_priv *codec_priv; struct device *dev = card->dev; + int codec_idx; int ret; if (fsl_asoc_card_is_ac97(priv)) { @@ -526,32 +608,39 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) return 0; } - ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, - codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) { - dev_err(dev, "failed to set sysclk in %s\n", __func__); - return ret; - } + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { + codec_priv = &priv->codec_priv[codec_idx]; - if (!IS_ERR_OR_NULL(codec_priv->mclk)) - clk_prepare_enable(codec_priv->mclk); + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set sysclk in %s\n", __func__); + return ret; + } + + if (!IS_ERR_OR_NULL(codec_priv->mclk)) + clk_prepare_enable(codec_priv->mclk); + } return 0; } static int fsl_asoc_card_probe(struct platform_device *pdev) { - struct device_node *cpu_np, *codec_np, *asrc_np; + struct device_node *cpu_np, *asrc_np; + struct snd_soc_dai_link_component *codec_comp; + struct device_node *codec_np[2]; struct device_node *np = pdev->dev.of_node; struct platform_device *asrc_pdev = NULL; struct device_node *bitclkprovider = NULL; struct device_node *frameprovider = NULL; struct platform_device *cpu_pdev; struct fsl_asoc_card_priv *priv; - struct device *codec_dev = NULL; - const char *codec_dai_name; - const char *codec_dev_name; + struct device *codec_dev[2] = { NULL, NULL }; + const char *codec_dai_name[2]; + const char *codec_dev_name[2]; u32 asrc_fmt = 0; + int codec_idx; u32 width; int ret; @@ -562,9 +651,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->pdev = pdev; cpu_np = of_parse_phandle(np, "audio-cpu", 0); - /* Give a chance to old DT binding */ + /* Give a chance to old DT bindings */ if (!cpu_np) cpu_np = of_parse_phandle(np, "ssi-controller", 0); + if (!cpu_np) + cpu_np = of_parse_phandle(np, "spdif-controller", 0); if (!cpu_np) { dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); ret = -EINVAL; @@ -578,21 +669,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto fail; } - codec_np = of_parse_phandle(np, "audio-codec", 0); - if (codec_np) { - struct platform_device *codec_pdev; - struct i2c_client *codec_i2c; + codec_np[0] = of_parse_phandle(np, "audio-codec", 0); + codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - codec_i2c = of_find_i2c_device_by_node(codec_np); - if (codec_i2c) { - codec_dev = &codec_i2c->dev; - codec_dev_name = codec_i2c->name; - } - if (!codec_dev) { - codec_pdev = of_find_device_by_node(codec_np); - if (codec_pdev) { - codec_dev = &codec_pdev->dev; - codec_dev_name = codec_pdev->name; + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + if (codec_np[codec_idx]) { + struct platform_device *codec_pdev; + struct i2c_client *codec_i2c; + + codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); + if (codec_i2c) { + codec_dev[codec_idx] = &codec_i2c->dev; + codec_dev_name[codec_idx] = codec_i2c->name; + } + if (!codec_dev[codec_idx]) { + codec_pdev = of_find_device_by_node(codec_np[codec_idx]); + if (codec_pdev) { + codec_dev[codec_idx] = &codec_pdev->dev; + codec_dev_name[codec_idx] = codec_pdev->name; + } } } } @@ -602,12 +697,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_pdev = of_find_device_by_node(asrc_np); /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ - if (codec_dev) { - struct clk *codec_clk = clk_get(codec_dev, NULL); + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + if (codec_dev[codec_idx]) { + struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); - if (!IS_ERR(codec_clk)) { - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); - clk_put(codec_clk); + if (!IS_ERR(codec_clk)) { + priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); + clk_put(codec_clk); + } } } @@ -620,36 +717,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + priv->dai_link[0].num_codecs = 1; + priv->dai_link[2].num_codecs = 1; priv->card.dapm_routes = audio_map; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); priv->card.driver_name = DRIVER_NAME; - priv->codec_priv.fll_id = -1; - priv->codec_priv.pll_id = -1; + for (codec_idx = 0; codec_idx < 2; codec_idx++) { + priv->codec_priv[codec_idx].fll_id = -1; + priv->codec_priv[codec_idx].pll_id = -1; + } /* Diversify the card configurations */ if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { - codec_dai_name = "cs42888"; - priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; - priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; + codec_dai_name[0] = "cs42888"; + priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; + priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { - codec_dai_name = "cs4271-hifi"; - priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; + codec_dai_name[0] = "cs4271-hifi"; + priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { - codec_dai_name = "sgtl5000"; - priv->codec_priv.mclk_id = SGTL5000_SYSCLK; + codec_dai_name[0] = "sgtl5000"; + priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { - codec_dai_name = "tlv320aic32x4-hifi"; + codec_dai_name[0] = "tlv320aic32x4-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { - codec_dai_name = "tlv320dac31xx-hifi"; + codec_dai_name[0] = "tlv320dac31xx-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; @@ -658,23 +759,23 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { - codec_dai_name = "wm8962"; - priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; - priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; - priv->codec_priv.pll_id = WM8962_FLL; + codec_dai_name[0] = "wm8962"; + priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; + priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; + priv->codec_priv[0].pll_id = WM8962_FLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { - codec_dai_name = "wm8960-hifi"; - priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; - priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; + codec_dai_name[0] = "wm8960-hifi"; + priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; + priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { - codec_dai_name = "ac97-hifi"; + codec_dai_name[0] = "ac97-hifi"; priv->dai_fmt = SND_SOC_DAIFMT_AC97; priv->card.dapm_routes = audio_map_ac97; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { - codec_dai_name = "fsl-mqs-dai"; + codec_dai_name[0] = "fsl-mqs-dai"; priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_NB_NF; @@ -683,7 +784,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { - codec_dai_name = "wm8524-hifi"; + codec_dai_name[0] = "wm8524-hifi"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; @@ -691,33 +792,37 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { - codec_dai_name = "si476x-codec"; + codec_dai_name[0] = "si476x-codec"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->card.dapm_routes = audio_map_rx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { - codec_dai_name = "wm8994-aif1"; + codec_dai_name[0] = "wm8994-aif1"; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; - priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1; - priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1; - priv->codec_priv.pll_id = WM8994_FLL1; - priv->codec_priv.free_freq = priv->codec_priv.mclk_freq; + priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; + priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; + priv->codec_priv[0].pll_id = WM8994_FLL1; + priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; priv->card.dapm_routes = NULL; priv->card.num_dapm_routes = 0; } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { - codec_dai_name = "nau8822-hifi"; - priv->codec_priv.mclk_id = NAU8822_CLK_MCLK; - priv->codec_priv.fll_id = NAU8822_CLK_PLL; - priv->codec_priv.pll_id = NAU8822_CLK_PLL; + codec_dai_name[0] = "nau8822-hifi"; + priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; + priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; + priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - if (codec_dev) - priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL); + if (codec_dev[0]) + priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { - codec_dai_name = "wm8904-hifi"; - priv->codec_priv.mclk_id = WM8904_FLL_MCLK; - priv->codec_priv.fll_id = WM8904_CLK_FLL; - priv->codec_priv.pll_id = WM8904_FLL_MCLK; + codec_dai_name[0] = "wm8904-hifi"; + priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; + priv->codec_priv[0].fll_id = WM8904_CLK_FLL; + priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; + } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { + ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); + if (ret) + goto asrc_fail; } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; @@ -728,18 +833,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) * Allow setting mclk-id from the device-tree node. Otherwise, the * default value for each card configuration is used. */ - of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id); + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + of_property_read_u32_index(np, "mclk-id", codec_idx, + &priv->codec_priv[codec_idx].mclk_id); + } /* Format info from DT is optional. */ snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider); if (bitclkprovider || frameprovider) { unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL); + bool codec_bitclkprovider = false; + bool codec_frameprovider = false; + + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + if (bitclkprovider && codec_np[codec_idx] == bitclkprovider) + codec_bitclkprovider = true; + if (frameprovider && codec_np[codec_idx] == frameprovider) + codec_frameprovider = true; + } - if (codec_np == bitclkprovider) - daifmt |= (codec_np == frameprovider) ? + if (codec_bitclkprovider) + daifmt |= (codec_frameprovider) ? SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC; else - daifmt |= (codec_np == frameprovider) ? + daifmt |= (codec_frameprovider) ? SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC; /* Override dai_fmt with value from DT */ @@ -755,7 +872,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) of_node_put(bitclkprovider); of_node_put(frameprovider); - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] + && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { dev_dbg(&pdev->dev, "failed to find codec device\n"); ret = -EPROBE_DEFER; goto asrc_fail; @@ -794,7 +912,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = snd_soc_of_parse_card_name(&priv->card, "model"); if (ret) { snprintf(priv->name, sizeof(priv->name), "%s-audio", - fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name); + fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); priv->card.name = priv->name; } priv->card.dai_link = priv->dai_link; @@ -816,11 +934,19 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* Normal DAI Link */ priv->dai_link[0].cpus->of_node = cpu_np; - priv->dai_link[0].codecs->dai_name = codec_dai_name; + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + codec_comp->dai_name = codec_dai_name[codec_idx]; + } + + // Old SPDIF DT binding support + if (codec_dai_name[0] == snd_soc_dummy_dlc.dai_name) + priv->dai_link[0].codecs[0].name = snd_soc_dummy_dlc.name; - if (!fsl_asoc_card_is_ac97(priv)) - priv->dai_link[0].codecs->of_node = codec_np; - else { + if (!fsl_asoc_card_is_ac97(priv)) { + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { + codec_comp->of_node = codec_np[codec_idx]; + } + } else { u32 idx; ret = of_property_read_u32(cpu_np, "cell-index", &idx); @@ -830,11 +956,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } - priv->dai_link[0].codecs->name = + priv->dai_link[0].codecs[0].name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); - if (!priv->dai_link[0].codecs->name) { + if (!priv->dai_link[0].codecs[0].name) { ret = -ENOMEM; goto asrc_fail; } @@ -848,10 +974,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) /* DPCM DAI Links only if ASRC exists */ priv->dai_link[1].cpus->of_node = asrc_np; priv->dai_link[1].platforms->of_node = asrc_np; - priv->dai_link[2].codecs->dai_name = codec_dai_name; - priv->dai_link[2].codecs->of_node = codec_np; - priv->dai_link[2].codecs->name = - priv->dai_link[0].codecs->name; + for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) { + codec_comp->dai_name = priv->dai_link[0].codecs[codec_idx].dai_name; + codec_comp->of_node = priv->dai_link[0].codecs[codec_idx].of_node; + codec_comp->name = priv->dai_link[0].codecs[codec_idx].name; + } priv->dai_link[2].cpus->of_node = cpu_np; priv->dai_link[2].dai_fmt = priv->dai_fmt; priv->card.num_links = 3; @@ -921,7 +1048,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) asrc_fail: of_node_put(asrc_np); - of_node_put(codec_np); + of_node_put(codec_np[0]); + of_node_put(codec_np[1]); put_device(&cpu_pdev->dev); fail: of_node_put(cpu_np); @@ -944,6 +1072,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8958", }, { .compatible = "fsl,imx-audio-nau8822", }, { .compatible = "fsl,imx-audio-wm8904", }, + { .compatible = "fsl,imx-audio-spdif", }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index ee2f6ad1f800..a6cbaa6364c7 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -261,7 +261,7 @@ static void fsl_aud2htx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) +static int fsl_aud2htx_runtime_suspend(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); @@ -271,7 +271,7 @@ static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) +static int fsl_aud2htx_runtime_resume(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); int ret; @@ -288,9 +288,8 @@ static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) } static const struct dev_pm_ops fsl_aud2htx_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, - fsl_aud2htx_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume, + NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -300,7 +299,7 @@ static struct platform_driver fsl_aud2htx_driver = { .remove_new = fsl_aud2htx_remove, .driver = { .name = "fsl-aud2htx", - .pm = &fsl_aud2htx_pm_ops, + .pm = pm_ptr(&fsl_aud2htx_pm_ops), .of_match_table = fsl_aud2htx_dt_ids, }, }; diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 0ab2c1962117..1671a3037c60 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -326,15 +326,6 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = { .rates = SNDRV_PCM_RATE_8000_96000, .formats = FSL_AUDMIX_FORMATS, }, - .capture = { - .stream_name = "AUDMIX-Capture-0", - .channels_min = 8, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 96000, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = FSL_AUDMIX_FORMATS, - }, .ops = &fsl_audmix_dai_ops, }, { @@ -349,8 +340,13 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = { .rates = SNDRV_PCM_RATE_8000_96000, .formats = FSL_AUDMIX_FORMATS, }, + .ops = &fsl_audmix_dai_ops, + }, + { + .id = 2, + .name = "audmix-2", .capture = { - .stream_name = "AUDMIX-Capture-1", + .stream_name = "AUDMIX-Capture-0", .channels_min = 8, .channels_max = 8, .rate_min = 8000, diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index ec53bda46a46..962f30912091 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1988,7 +1988,7 @@ static void fsl_easrc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev) +static int fsl_easrc_runtime_suspend(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; @@ -2005,7 +2005,7 @@ static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int fsl_easrc_runtime_resume(struct device *dev) +static int fsl_easrc_runtime_resume(struct device *dev) { struct fsl_asrc *easrc = dev_get_drvdata(dev); struct fsl_easrc_priv *easrc_priv = easrc->private; @@ -2086,9 +2086,7 @@ disable_mem_clk: } static const struct dev_pm_ops fsl_easrc_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, - fsl_easrc_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -2098,7 +2096,7 @@ static struct platform_driver fsl_easrc_driver = { .remove_new = fsl_easrc_remove, .driver = { .name = "fsl-easrc", - .pm = &fsl_easrc_pm_ops, + .pm = pm_ptr(&fsl_easrc_pm_ops), .of_match_table = fsl_easrc_dt_ids, }, }; diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 60929c36a0e3..c95b84a54dc4 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -28,10 +28,16 @@ #define MQS_CLK_DIV_MASK (0xFF << 0) #define MQS_CLK_DIV_SHIFT (0) +enum reg_type { + TYPE_REG_OWN, /* module own register space */ + TYPE_REG_GPR, /* register in GPR space */ + TYPE_REG_SM, /* System Manager controls the register */ +}; + /** * struct fsl_mqs_soc_data - soc specific data * - * @use_gpr: control register is in General Purpose Register group + * @type: control register space type * @ctrl_off: control register offset * @en_mask: enable bit mask * @en_shift: enable bit shift @@ -43,7 +49,7 @@ * @div_shift: clock divider bit shift */ struct fsl_mqs_soc_data { - bool use_gpr; + enum reg_type type; int ctrl_off; int en_mask; int en_shift; @@ -200,7 +206,7 @@ static int fsl_mqs_probe(struct platform_device *pdev) */ mqs_priv->soc = of_device_get_match_data(&pdev->dev); - if (mqs_priv->soc->use_gpr) { + if (mqs_priv->soc->type == TYPE_REG_GPR) { gpr_np = of_parse_phandle(np, "gpr", 0); if (!gpr_np) { dev_err(&pdev->dev, "failed to get gpr node by phandle\n"); @@ -304,7 +310,7 @@ static const struct dev_pm_ops fsl_mqs_pm_ops = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = { - .use_gpr = false, + .type = TYPE_REG_OWN, .ctrl_off = REG_MQS_CTRL, .en_mask = MQS_EN_MASK, .en_shift = MQS_EN_SHIFT, @@ -317,7 +323,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = { - .use_gpr = true, + .type = TYPE_REG_GPR, .ctrl_off = IOMUXC_GPR2, .en_mask = IMX6SX_GPR2_MQS_EN_MASK, .en_shift = IMX6SX_GPR2_MQS_EN_SHIFT, @@ -330,7 +336,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { - .use_gpr = true, + .type = TYPE_REG_GPR, .ctrl_off = 0x20, .en_mask = BIT(1), .en_shift = 1, @@ -342,10 +348,38 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { .div_shift = 8, }; +static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = { + .type = TYPE_REG_SM, + .ctrl_off = 0x88, + .en_mask = BIT(1), + .en_shift = 1, + .rst_mask = BIT(2), + .rst_shift = 2, + .osr_mask = BIT(3), + .osr_shift = 3, + .div_mask = GENMASK(15, 8), + .div_shift = 8, +}; + +static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = { + .type = TYPE_REG_GPR, + .ctrl_off = 0x0, + .en_mask = BIT(2), + .en_shift = 2, + .rst_mask = BIT(3), + .rst_shift = 3, + .osr_mask = BIT(4), + .osr_shift = 4, + .div_mask = GENMASK(16, 9), + .div_shift = 9, +}; + static const struct of_device_id fsl_mqs_dt_ids[] = { { .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data }, { .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data }, { .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data }, + { .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data }, + { .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids); diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index bfaaa451735b..8668abd35208 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -17,13 +17,23 @@ #include <sound/pcm_params.h> #include <sound/soc.h> +struct qmc_dai_chan { + struct qmc_dai_prtd *prtd_tx; + struct qmc_dai_prtd *prtd_rx; + struct qmc_chan *qmc_chan; +}; + struct qmc_dai { char *name; int id; struct device *dev; - struct qmc_chan *qmc_chan; unsigned int nb_tx_ts; unsigned int nb_rx_ts; + + unsigned int nb_chans_avail; + unsigned int nb_chans_used_tx; + unsigned int nb_chans_used_rx; + struct qmc_dai_chan *chans; }; struct qmc_audio { @@ -35,11 +45,19 @@ struct qmc_audio { struct qmc_dai_prtd { struct qmc_dai *qmc_dai; - dma_addr_t dma_buffer_start; - dma_addr_t period_ptr_submitted; - dma_addr_t period_ptr_ended; - dma_addr_t dma_buffer_end; - size_t period_size; + + snd_pcm_uframes_t buffer_ended; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + + dma_addr_t ch_dma_addr_start; + dma_addr_t ch_dma_addr_current; + dma_addr_t ch_dma_addr_end; + size_t ch_dma_size; + size_t ch_dma_offset; + + unsigned int channels; + DECLARE_BITMAP(chans_pending, 64); struct snd_pcm_substream *substream; }; @@ -54,10 +72,22 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component, return ret; snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev, - 64*1024, 64*1024); + 64 * 1024, 64 * 1024); return 0; } +static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access) +{ + switch (access) { + case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: + case SNDRV_PCM_ACCESS_RW_INTERLEAVED: + return true; + default: + break; + } + return false; +} + static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -65,66 +95,143 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct qmc_dai_prtd *prtd = substream->runtime->private_data; - prtd->dma_buffer_start = runtime->dma_addr; - prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params); - prtd->period_size = params_period_bytes(params); - prtd->period_ptr_submitted = prtd->dma_buffer_start; - prtd->period_ptr_ended = prtd->dma_buffer_start; + /* + * In interleaved mode, the driver uses one QMC channel for all audio + * channels whereas in non-interleaved mode, it uses one QMC channel per + * audio channel. + */ + prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ? + 1 : params_channels(params); + prtd->substream = substream; + prtd->buffer_ended = 0; + prtd->buffer_size = params_buffer_size(params); + prtd->period_size = params_period_size(params); + + prtd->ch_dma_addr_start = runtime->dma_addr; + prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels; + prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset; + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; + prtd->ch_dma_size = params_period_bytes(params) / prtd->channels; + return 0; } -static void qmc_audio_pcm_write_complete(void *context) +static void qmc_audio_pcm_write_complete(void *context); + +static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd) { - struct qmc_dai_prtd *prtd = context; + unsigned int i; int ret; - prtd->period_ptr_ended += prtd->period_size; - if (prtd->period_ptr_ended >= prtd->dma_buffer_end) - prtd->period_ptr_ended = prtd->dma_buffer_start; - - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + for (i = 0; i < prtd->channels; i++) { + bitmap_set(prtd->chans_pending, i, 1); - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); - if (ret) { - dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n", - ret); + ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan, + prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, + prtd->ch_dma_size, + qmc_audio_pcm_write_complete, + &prtd->qmc_dai->chans[i]); + if (ret) { + dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n", + i, ret); + bitmap_clear(prtd->chans_pending, i, 1); + return ret; + } } + return 0; +} + +static void qmc_audio_pcm_write_complete(void *context) +{ + struct qmc_dai_chan *chan = context; + struct qmc_dai_prtd *prtd; + + prtd = chan->prtd_tx; + + /* Mark the current channel as completed */ + bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); + + /* + * All QMC channels involved must have completed their transfer before + * submitting a new one. + */ + if (!bitmap_empty(prtd->chans_pending, 64)) + return; + + prtd->buffer_ended += prtd->period_size; + if (prtd->buffer_ended >= prtd->buffer_size) + prtd->buffer_ended = 0; + + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; + + qmc_audio_pcm_write_submit(prtd); + snd_pcm_period_elapsed(prtd->substream); } -static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) +static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags); + +static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd) { - struct qmc_dai_prtd *prtd = context; + unsigned int i; int ret; - if (length != prtd->period_size) { - dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", - length, prtd->period_size); + for (i = 0; i < prtd->channels; i++) { + bitmap_set(prtd->chans_pending, i, 1); + + ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan, + prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, + prtd->ch_dma_size, + qmc_audio_pcm_read_complete, + &prtd->qmc_dai->chans[i]); + if (ret) { + dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n", + i, ret); + bitmap_clear(prtd->chans_pending, i, 1); + return ret; + } } - prtd->period_ptr_ended += prtd->period_size; - if (prtd->period_ptr_ended >= prtd->dma_buffer_end) - prtd->period_ptr_ended = prtd->dma_buffer_start; + return 0; +} + +static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) +{ + struct qmc_dai_chan *chan = context; + struct qmc_dai_prtd *prtd; + + prtd = chan->prtd_rx; - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + /* Mark the current channel as completed */ + bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); - if (ret) { - dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n", - ret); + if (length != prtd->ch_dma_size) { + dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", + length, prtd->ch_dma_size); } + /* + * All QMC channels involved must have completed their transfer before + * submitting a new one. + */ + if (!bitmap_empty(prtd->chans_pending, 64)) + return; + + prtd->buffer_ended += prtd->period_size; + if (prtd->buffer_ended >= prtd->buffer_size) + prtd->buffer_ended = 0; + + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; + + qmc_audio_pcm_read_submit(prtd); + snd_pcm_period_elapsed(prtd->substream); } @@ -132,6 +239,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct qmc_dai_prtd *prtd = substream->runtime->private_data; + unsigned int i; int ret; if (!prtd->qmc_dai) { @@ -141,56 +249,43 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, switch (cmd) { case SNDRV_PCM_TRIGGER_START: + bitmap_zero(prtd->chans_pending, 64); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < prtd->channels; i++) + prtd->qmc_dai->chans[i].prtd_tx = prtd; + /* Submit first chunk ... */ - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_write_submit(prtd); + if (ret) return ret; - } /* ... prepare next one ... */ - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_write_complete, prtd); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_write_submit(prtd); + if (ret) return ret; - } } else { + for (i = 0; i < prtd->channels; i++) + prtd->qmc_dai->chans[i].prtd_rx = prtd; + /* Submit first chunk ... */ - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); - if (ret) { - dev_err(component->dev, "read_submit failed %d\n", - ret); + ret = qmc_audio_pcm_read_submit(prtd); + if (ret) return ret; - } /* ... prepare next one ... */ - prtd->period_ptr_submitted += prtd->period_size; - if (prtd->period_ptr_submitted >= prtd->dma_buffer_end) - prtd->period_ptr_submitted = prtd->dma_buffer_start; + prtd->ch_dma_addr_current += prtd->ch_dma_size; + if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end) + prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; /* ... and send it */ - ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan, - prtd->period_ptr_submitted, prtd->period_size, - qmc_audio_pcm_read_complete, prtd); - if (ret) { - dev_err(component->dev, "write_submit failed %d\n", - ret); + ret = qmc_audio_pcm_read_submit(prtd); + if (ret) return ret; - } } break; @@ -215,13 +310,12 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone { struct qmc_dai_prtd *prtd = substream->runtime->private_data; - return bytes_to_frames(substream->runtime, - prtd->period_ptr_ended - prtd->dma_buffer_start); + return prtd->buffer_ended; } static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component, - const struct of_phandle_args *args, - const char **dai_name) + const struct of_phandle_args *args, + const char **dai_name) { struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev); struct snd_soc_dai_driver *dai_driver; @@ -243,12 +337,13 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_NONINTERLEAVED | SNDRV_PCM_INFO_PAUSE, .period_bytes_min = 32, - .period_bytes_max = 64*1024, + .period_bytes_max = 64 * 1024, .periods_min = 2, - .periods_max = 2*1024, - .buffer_bytes_max = 64*1024, + .periods_max = 2 * 1024, + .buffer_bytes_max = 64 * 1024, }; static int qmc_audio_pcm_open(struct snd_soc_component *component, @@ -266,7 +361,7 @@ static int qmc_audio_pcm_open(struct snd_soc_component *component, return ret; prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (prtd == NULL) + if (!prtd) return -ENOMEM; runtime->private_data = prtd; @@ -329,13 +424,13 @@ static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai, ch.max = nb_ts; break; case 16: - ch.max = nb_ts/2; + ch.max = nb_ts / 2; break; case 32: - ch.max = nb_ts/4; + ch.max = nb_ts / 4; break; case 64: - ch.max = nb_ts/8; + ch.max = nb_ts / 8; break; default: dev_err(qmc_dai->dev, "format physical width %u not supported\n", @@ -356,9 +451,8 @@ static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts); } -static int qmc_dai_hw_rule_capture_channels_by_format( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; @@ -394,42 +488,31 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, return snd_mask_refine(f_old, &f_new); } -static int qmc_dai_hw_rule_playback_format_by_channels( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts); } -static int qmc_dai_hw_rule_capture_format_by_channels( - struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { struct qmc_dai *qmc_dai = rule->private; return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts); } -static int qmc_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, + struct qmc_dai *qmc_dai) { - struct qmc_dai_prtd *prtd = substream->runtime->private_data; snd_pcm_hw_rule_func_t hw_rule_channels_by_format; snd_pcm_hw_rule_func_t hw_rule_format_by_channels; - struct qmc_dai *qmc_dai; unsigned int frame_bits; + u64 access; int ret; - qmc_dai = qmc_dai_get_data(dai); - if (!qmc_dai) { - dev_err(dai->dev, "Invalid dai\n"); - return -EINVAL; - } - - prtd->qmc_dai = qmc_dai; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format; hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels; @@ -444,7 +527,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, hw_rule_channels_by_format, qmc_dai, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) { - dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + dev_err(qmc_dai->dev, "Failed to add channels rule (%d)\n", ret); return ret; } @@ -452,27 +535,86 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream, hw_rule_format_by_channels, qmc_dai, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret) { - dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + dev_err(qmc_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(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | + 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED; + ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, + access); + if (ret) { + dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret); return ret; } + return 0; +} + +static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream, + struct qmc_dai *qmc_dai) +{ + unsigned int frame_bits; + u64 access; + int ret; + + frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ? + qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8; 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); + dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | + 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, + access); + if (ret) { + dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret); return ret; } return 0; } +static int qmc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qmc_dai_prtd *prtd = substream->runtime->private_data; + struct qmc_dai *qmc_dai; + + qmc_dai = qmc_dai_get_data(dai); + if (!qmc_dai) { + dev_err(dai->dev, "Invalid dai\n"); + return -EINVAL; + } + + prtd->qmc_dai = qmc_dai; + + return qmc_dai->nb_chans_avail > 1 ? + qmc_dai_constraints_noninterleaved(substream, qmc_dai) : + qmc_dai_constraints_interleaved(substream, qmc_dai); +} + static int qmc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct qmc_chan_param chan_param = {0}; + unsigned int nb_chans_used; struct qmc_dai *qmc_dai; + unsigned int i; int ret; qmc_dai = qmc_dai_get_data(dai); @@ -481,15 +623,34 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * In interleaved mode, the driver uses one QMC channel for all audio + * channels whereas in non-interleaved mode, it uses one QMC channel per + * audio channel. + */ + nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ? + 1 : params_channels(params); + + if (nb_chans_used > qmc_dai->nb_chans_avail) { + dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n", + nb_chans_used, qmc_dai->nb_chans_avail); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { chan_param.mode = QMC_TRANSPARENT; - chan_param.transp.max_rx_buf_size = params_period_bytes(params); - ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param); - if (ret) { - dev_err(dai->dev, "set param failed %d\n", - ret); - return ret; + chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used; + for (i = 0; i < nb_chans_used; i++) { + ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param); + if (ret) { + dev_err(dai->dev, "chans[%u], set param failed %d\n", + i, ret); + return ret; + } } + qmc_dai->nb_chans_used_rx = nb_chans_used; + } else { + qmc_dai->nb_chans_used_tx = nb_chans_used; } return 0; @@ -498,9 +659,12 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + unsigned int nb_chans_used; struct qmc_dai *qmc_dai; + unsigned int i; int direction; - int ret; + int ret = 0; + int ret_tmp; qmc_dai = qmc_dai_get_data(dai); if (!qmc_dai) { @@ -508,30 +672,50 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return -EINVAL; } - direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - QMC_CHAN_WRITE : QMC_CHAN_READ; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = QMC_CHAN_WRITE; + nb_chans_used = qmc_dai->nb_chans_used_tx; + } else { + direction = QMC_CHAN_READ; + nb_chans_used = qmc_dai->nb_chans_used_rx; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = qmc_chan_start(qmc_dai->qmc_chan, direction); - if (ret) - return ret; + for (i = 0; i < nb_chans_used; i++) { + ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction); + if (ret) + goto err_stop; + } break; case SNDRV_PCM_TRIGGER_STOP: - ret = qmc_chan_stop(qmc_dai->qmc_chan, direction); - if (ret) - return ret; - ret = qmc_chan_reset(qmc_dai->qmc_chan, direction); + /* Stop and reset all QMC channels and return the first error encountered */ + for (i = 0; i < nb_chans_used; i++) { + ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + if (ret_tmp) + continue; + + ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + } if (ret) return ret; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = qmc_chan_stop(qmc_dai->qmc_chan, direction); + /* Stop all QMC channels and return the first error encountered */ + for (i = 0; i < nb_chans_used; i++) { + ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + if (!ret) + ret = ret_tmp; + } if (ret) return ret; break; @@ -541,6 +725,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, } return 0; + +err_stop: + while (i--) { + qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); + qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); + } + return ret; } static const struct snd_soc_dai_ops qmc_dai_ops = { @@ -549,7 +740,7 @@ static const struct snd_soc_dai_ops qmc_dai_ops = { .hw_params = qmc_dai_hw_params, }; -static u64 qmc_audio_formats(u8 nb_ts) +static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved) { unsigned int format_width; unsigned int chan_width; @@ -581,15 +772,29 @@ static u64 qmc_audio_formats(u8 nb_ts) if (format_width > chan_width || chan_width % format_width) continue; + /* + * In non interleaved mode, we can only support formats that + * can fit only 1 time in the channel + */ + if (is_noninterleaved && format_width != chan_width) + continue; + formats_mask |= pcm_format_to_bits(format); } return formats_mask; } static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np, - struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver) + struct qmc_dai *qmc_dai, + struct snd_soc_dai_driver *qmc_soc_dai_driver) { struct qmc_chan_info info; + unsigned long rx_fs_rate; + unsigned long tx_fs_rate; + unsigned int nb_tx_ts; + unsigned int nb_rx_ts; + unsigned int i; + int count; u32 val; int ret; @@ -604,57 +809,108 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d", np->parent->name, qmc_dai->id); + if (!qmc_dai->name) + return -ENOMEM; - qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np, - "fsl,qmc-chan"); - if (IS_ERR(qmc_dai->qmc_chan)) { - ret = PTR_ERR(qmc_dai->qmc_chan); - return dev_err_probe(qmc_audio->dev, ret, - "dai %d get QMC channel failed\n", qmc_dai->id); - } + count = qmc_chan_count_phandles(np, "fsl,qmc-chan"); + if (count < 0) + return dev_err_probe(qmc_audio->dev, count, + "dai %d get number of QMC channel failed\n", qmc_dai->id); + if (!count) + return dev_err_probe(qmc_audio->dev, -EINVAL, + "dai %d no QMC channel defined\n", qmc_dai->id); - qmc_soc_dai_driver->id = qmc_dai->id; - qmc_soc_dai_driver->name = qmc_dai->name; + qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL); + if (!qmc_dai->chans) + return -ENOMEM; - ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info); - if (ret) { - dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n", - qmc_dai->id, ret); - return ret; - } - dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n", - qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts); + for (i = 0; i < count; i++) { + qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, + "fsl,qmc-chan", i); + if (IS_ERR(qmc_dai->chans[i].qmc_chan)) { + return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan), + "dai %d get QMC channel %d failed\n", qmc_dai->id, i); + } - if (info.mode != QMC_TRANSPARENT) { - dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n", - qmc_dai->id, info.mode); - return -EINVAL; + ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info); + if (ret) { + dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n", + qmc_dai->id, i, ret); + return ret; + } + dev_info(qmc_audio->dev, "dai %d QMC channel %d mode %d, nb_tx_ts %u, nb_rx_ts %u\n", + qmc_dai->id, i, info.mode, info.nb_tx_ts, info.nb_rx_ts); + + if (info.mode != QMC_TRANSPARENT) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n", + qmc_dai->id, i, info.mode); + return -EINVAL; + } + + /* + * All channels must have the same number of Tx slots and the + * same numbers of Rx slots. + */ + if (i == 0) { + nb_tx_ts = info.nb_tx_ts; + nb_rx_ts = info.nb_rx_ts; + tx_fs_rate = info.tx_fs_rate; + rx_fs_rate = info.rx_fs_rate; + } else { + if (nb_tx_ts != info.nb_tx_ts) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n", + qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts); + return -EINVAL; + } + if (nb_rx_ts != info.nb_rx_ts) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n", + qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts); + return -EINVAL; + } + if (tx_fs_rate != info.tx_fs_rate) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n", + qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate); + return -EINVAL; + } + if (rx_fs_rate != info.rx_fs_rate) { + dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n", + qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate); + return -EINVAL; + } + } } - qmc_dai->nb_tx_ts = info.nb_tx_ts; - qmc_dai->nb_rx_ts = info.nb_rx_ts; + + qmc_dai->nb_chans_avail = count; + qmc_dai->nb_tx_ts = nb_tx_ts * count; + qmc_dai->nb_rx_ts = nb_rx_ts * count; + + qmc_soc_dai_driver->id = qmc_dai->id; + qmc_soc_dai_driver->name = qmc_dai->name; qmc_soc_dai_driver->playback.channels_min = 0; qmc_soc_dai_driver->playback.channels_max = 0; - if (qmc_dai->nb_tx_ts) { + if (nb_tx_ts) { qmc_soc_dai_driver->playback.channels_min = 1; - qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts; + qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts; } - qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts); + qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts, + count > 1 ? true : false); qmc_soc_dai_driver->capture.channels_min = 0; qmc_soc_dai_driver->capture.channels_max = 0; - if (qmc_dai->nb_rx_ts) { + if (nb_rx_ts) { qmc_soc_dai_driver->capture.channels_min = 1; - qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts; + qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts; } - qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts); + qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts, + count > 1 ? true : false); - qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate); - qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate; - qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate; - qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate); - qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate; - qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate; + qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate); + qmc_soc_dai_driver->playback.rate_min = tx_fs_rate; + qmc_soc_dai_driver->playback.rate_max = tx_fs_rate; + qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate); + qmc_soc_dai_driver->capture.rate_min = rx_fs_rate; + qmc_soc_dai_driver->capture.rate_max = rx_fs_rate; qmc_soc_dai_driver->ops = &qmc_dai_ops; @@ -702,7 +958,6 @@ static int qmc_audio_probe(struct platform_device *pdev) i++; } - platform_set_drvdata(pdev, qmc_audio); ret = devm_snd_soc_register_component(qmc_audio->dev, diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index bc41a0666856..467d6bc9f956 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -175,6 +175,14 @@ static const struct fsl_rpmsg_soc_data imx93_data = { SNDRV_PCM_FMTBIT_S32_LE, }; +static const struct fsl_rpmsg_soc_data imx95_data = { + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, +}; + static const struct of_device_id fsl_rpmsg_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data}, { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data}, @@ -182,6 +190,7 @@ static const struct of_device_id fsl_rpmsg_ids[] = { { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data}, { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data}, { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data}, + { .compatible = "fsl,imx95-rpmsg-audio", .data = &imx95_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 0e2c31439670..d03b0172b8ad 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -357,18 +357,18 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_BP_FP: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_consumer_mode = false; + sai->is_consumer_mode[tx] = false; break; case SND_SOC_DAIFMT_BC_FC: - sai->is_consumer_mode = true; + sai->is_consumer_mode[tx] = true; break; case SND_SOC_DAIFMT_BP_FC: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; - sai->is_consumer_mode = false; + sai->is_consumer_mode[tx] = false; break; case SND_SOC_DAIFMT_BC_FP: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_consumer_mode = true; + sai->is_consumer_mode[tx] = true; break; default: return -EINVAL; @@ -400,6 +400,16 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return ret; } +static int fsl_sai_set_dai_fmt_tx(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true); +} + +static int fsl_sai_set_dai_fmt_rx(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false); +} + static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); @@ -412,7 +422,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) bool support_1_1_ratio = sai->verid.version >= 0x0301; /* Don't apply to consumer mode */ - if (sai->is_consumer_mode) + if (sai->is_consumer_mode[tx]) return 0; /* @@ -575,7 +585,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, } } - if (!sai->is_consumer_mode) { + if (!sai->is_consumer_mode[tx]) { ret = fsl_sai_set_bclk(cpu_dai, tx, bclk); if (ret) return ret; @@ -613,7 +623,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, * RCR5(TCR5) for playback(capture), or there will be sync error. */ - if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) { + if (!sai->is_consumer_mode[tx] && fsl_sai_dir_is_synced(sai, adir)) { regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | FSL_SAI_CR4_CHMOD_MASK, @@ -683,7 +693,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, * FSD_MSTR bit for this specific case. */ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output && - !sai->is_consumer_mode) + !sai->is_consumer_mode[tx]) regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), FSL_SAI_CR4_FSD_MSTR, 0); @@ -697,7 +707,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, /* Enable FSD_MSTR after configuring word width */ if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output && - !sai->is_consumer_mode) + !sai->is_consumer_mode[tx]) regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR); @@ -720,8 +730,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_consumer_mode && - sai->mclk_streams & BIT(substream->stream)) { + if (!sai->is_consumer_mode[tx] && + sai->mclk_streams & BIT(substream->stream)) { clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); sai->mclk_streams &= ~BIT(substream->stream); } @@ -759,7 +769,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) * This is a hardware bug, and will be fix in the * next sai version. */ - if (!sai->is_consumer_mode) { + if (!sai->is_consumer_mode[tx]) { /* Software Reset */ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */ @@ -914,6 +924,30 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .startup = fsl_sai_startup, }; +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt_tx, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt_rx, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + static int fsl_sai_dai_resume(struct snd_soc_component *component) { struct fsl_sai *sai = snd_soc_component_get_drvdata(component); @@ -931,26 +965,55 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component) return 0; } -static struct snd_soc_dai_driver fsl_sai_dai_template = { - .playback = { - .stream_name = "CPU-Playback", - .channels_min = 1, - .channels_max = 32, - .rate_min = 8000, - .rate_max = 2822400, - .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_SAI_FORMATS, +static struct snd_soc_dai_driver fsl_sai_dai_template[] = { + { + .name = "sai-tx-rx", + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, + }, + { + .name = "sai-tx", + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_tx_ops, }, - .capture = { - .stream_name = "CPU-Capture", - .channels_min = 1, - .channels_max = 32, - .rate_min = 8000, - .rate_max = 2822400, - .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_SAI_FORMATS, + { + .name = "sai-rx", + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 32, + .rate_min = 8000, + .rate_max = 2822400, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_rx_ops, }, - .ops = &fsl_sai_pcm_dai_ops, }; static const struct snd_soc_component_driver fsl_component = { @@ -1399,15 +1462,15 @@ static int fsl_sai_probe(struct platform_device *pdev) return ret; } - memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template, - sizeof(fsl_sai_dai_template)); + memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template, + sizeof(*fsl_sai_dai_template) * ARRAY_SIZE(fsl_sai_dai_template)); /* Sync Tx with Rx as default by following old DT binding */ sai->synchronous[RX] = true; sai->synchronous[TX] = false; - sai->cpu_dai_drv.symmetric_rate = 1; - sai->cpu_dai_drv.symmetric_channels = 1; - sai->cpu_dai_drv.symmetric_sample_bits = 1; + sai->cpu_dai_drv[0].symmetric_rate = 1; + sai->cpu_dai_drv[0].symmetric_channels = 1; + sai->cpu_dai_drv[0].symmetric_sample_bits = 1; if (of_property_read_bool(np, "fsl,sai-synchronous-rx") && of_property_read_bool(np, "fsl,sai-asynchronous")) { @@ -1424,9 +1487,9 @@ static int fsl_sai_probe(struct platform_device *pdev) /* Discard all settings for asynchronous mode */ sai->synchronous[RX] = false; sai->synchronous[TX] = false; - sai->cpu_dai_drv.symmetric_rate = 0; - sai->cpu_dai_drv.symmetric_channels = 0; - sai->cpu_dai_drv.symmetric_sample_bits = 0; + sai->cpu_dai_drv[0].symmetric_rate = 0; + sai->cpu_dai_drv[0].symmetric_channels = 0; + sai->cpu_dai_drv[0].symmetric_sample_bits = 0; } sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output"); @@ -1505,7 +1568,7 @@ static int fsl_sai_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_component(dev, &fsl_component, - &sai->cpu_dai_drv, 1); + sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template)); if (ret) goto err_pm_get_sync; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 550df87b6a06..dadbd16ee394 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -282,7 +282,7 @@ struct fsl_sai { struct clk *pll11k_clk; struct resource *res; - bool is_consumer_mode; + bool is_consumer_mode[2]; bool is_lsb_first; bool is_dsp_mode; bool is_pdm_mode; @@ -299,7 +299,7 @@ struct fsl_sai { unsigned int bclk_ratio; const struct fsl_sai_soc_data *soc_data; - struct snd_soc_dai_driver cpu_dai_drv; + struct snd_soc_dai_driver cpu_dai_drv[3]; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; struct fsl_sai_verid verid; diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index c46f64557a7f..bf9a4e90978e 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -15,14 +15,22 @@ #include <sound/pcm_params.h> #include "fsl_xcvr.h" +#include "fsl_utils.h" #include "imx-pcm.h" #define FSL_XCVR_CAPDS_SIZE 256 +enum fsl_xcvr_pll_verison { + PLL_MX8MP, + PLL_MX95, +}; + struct fsl_xcvr_soc_data { const char *fw_name; bool spdif_only; bool use_edma; + bool use_phy; + enum fsl_xcvr_pll_verison pll_ver; }; struct fsl_xcvr { @@ -33,6 +41,8 @@ struct fsl_xcvr { struct clk *pll_ipg_clk; struct clk *phy_clk; struct clk *spba_clk; + struct clk *pll8k_clk; + struct clk *pll11k_clk; struct reset_control *reset; u8 streams; u32 mode; @@ -262,10 +272,10 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) { struct device *dev = &xcvr->pdev->dev; - u32 i, div = 0, log2; + u32 i, div = 0, log2, val; int ret; - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) return 0; for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) { @@ -288,45 +298,62 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) return ret; } - /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET, - FSL_XCVR_PLL_BANDGAP_EN_VBG, 0); - - /* PLL: CTRL0: DIV_INTEGER */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0); - /* PLL: NUMERATOR: MFN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0); - /* PLL: DENOMINATOR: MFD */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0); - /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0); - udelay(25); - /* PLL: CTRL0: Clear Hold Ring Off */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR, - FSL_XCVR_PLL_CTRL0_HROFF, 0); - udelay(100); - if (tx) { /* TX is enabled for SPDIF only */ - /* PLL: POSTDIV: PDIV0 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 0), 0); - /* PLL: CTRL_SET: CLKMUX0_EN */ + switch (xcvr->soc_data->pll_ver) { + case PLL_MX8MP: + /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET, + FSL_XCVR_PLL_BANDGAP_EN_VBG, 0); + + /* PLL: CTRL0: DIV_INTEGER */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0); + /* PLL: NUMERATOR: MFN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0); + /* PLL: DENOMINATOR: MFD */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0); + /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM0_EN, 0); - } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ - /* PLL: POSTDIV: PDIV1 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 1), 0); - /* PLL: CTRL_SET: CLKMUX1_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM1_EN, 0); - } else { /* SPDIF / ARC RX */ - /* PLL: POSTDIV: PDIV2 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 2), 0); - /* PLL: CTRL_SET: CLKMUX2_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0); + udelay(25); + /* PLL: CTRL0: Clear Hold Ring Off */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR, + FSL_XCVR_PLL_CTRL0_HROFF, 0); + udelay(100); + if (tx) { /* TX is enabled for SPDIF only */ + /* PLL: POSTDIV: PDIV0 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 0), 0); + /* PLL: CTRL_SET: CLKMUX0_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM0_EN, 0); + } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ + /* PLL: POSTDIV: PDIV1 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 1), 0); + /* PLL: CTRL_SET: CLKMUX1_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM1_EN, 0); + } else { /* SPDIF / ARC RX */ + /* PLL: POSTDIV: PDIV2 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 2), 0); + /* PLL: CTRL_SET: CLKMUX2_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + } + break; + case PLL_MX95: + val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DIV, val, 0); + val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_NUMERATOR, val, 0); + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DENOMINATOR, + fsl_xcvr_pll_cfg[i].mfd, 0); + val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN; + fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_CTRL, val, 0); + break; + default: + dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver); + return -EINVAL; } if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */ @@ -362,6 +389,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) freq = xcvr->soc_data->spdif_only ? freq / 5 : freq; clk_disable_unprepare(xcvr->phy_clk); + fsl_asoc_reparent_pll_clocks(dev, xcvr->phy_clk, + xcvr->pll8k_clk, xcvr->pll11k_clk, freq); ret = clk_set_rate(xcvr->phy_clk, freq); if (ret < 0) { dev_err(dev, "Error while setting AUD PLL rate: %d\n", ret); @@ -373,7 +402,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) return ret; } - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) return 0; /* Release AI interface from reset */ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, @@ -500,16 +529,6 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, break; } - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); - if (ret < 0) { - dev_err(dai->dev, "Error while setting IER0: %d\n", ret); - return ret; - } - - /* set DPATH RESET */ - m_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); - v_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, m_ctl, v_ctl); if (ret < 0) { dev_err(dai->dev, "Error while setting EXT_CTRL: %d\n", ret); @@ -650,6 +669,15 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* set DPATH RESET */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx), + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); + return ret; + } + if (tx) { switch (xcvr->mode) { case FSL_XCVR_MODE_EARC: @@ -682,6 +710,13 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); + if (ret < 0) { + dev_err(dai->dev, "Error while setting IER0: %d\n", ret); + return ret; + } + /* clear DPATH RESET */ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, FSL_XCVR_EXT_CTRL_DPTH_RESET(tx), @@ -704,6 +739,13 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); + return ret; + } + if (tx) { switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: @@ -1017,7 +1059,7 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) || reg > FSL_XCVR_TX_DPTH_BCRR) return false; @@ -1090,7 +1132,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); - if (xcvr->soc_data->spdif_only) + if (!xcvr->soc_data->use_phy) if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) return false; switch (reg) { @@ -1234,6 +1276,8 @@ static irqreturn_t irq0_isr(int irq, void *devid) static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = { .fw_name = "imx/xcvr/xcvr-imx8mp.bin", + .use_phy = true, + .pll_ver = PLL_MX8MP, }; static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { @@ -1241,9 +1285,17 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = { .use_edma = true, }; +static const struct fsl_xcvr_soc_data fsl_xcvr_imx95_data = { + .spdif_only = true, + .use_phy = true, + .use_edma = true, + .pll_ver = PLL_MX95, +}; + static const struct of_device_id fsl_xcvr_dt_ids[] = { { .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data }, { .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data}, + { .compatible = "fsl,imx95-xcvr", .data = &fsl_xcvr_imx95_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids); @@ -1287,6 +1339,9 @@ static int fsl_xcvr_probe(struct platform_device *pdev) return PTR_ERR(xcvr->pll_ipg_clk); } + fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk, + &xcvr->pll11k_clk); + xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram"); if (IS_ERR(xcvr->ram_addr)) return PTR_ERR(xcvr->ram_addr); @@ -1364,21 +1419,11 @@ static void fsl_xcvr_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) +static int fsl_xcvr_runtime_suspend(struct device *dev) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; - /* - * Clear interrupts, when streams starts or resumes after - * suspend, interrupts are enabled in prepare(), so no need - * to enable interrupts in resume(). - */ - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, 0); - if (ret < 0) - dev_err(dev, "Failed to clear IER0: %d\n", ret); - if (!xcvr->soc_data->spdif_only) { /* Assert M0+ reset */ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, @@ -1398,7 +1443,7 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev) +static int fsl_xcvr_runtime_resume(struct device *dev) { struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; @@ -1483,9 +1528,7 @@ stop_ipg_clk: } static const struct dev_pm_ops fsl_xcvr_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, - fsl_xcvr_runtime_resume, - NULL) + RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; @@ -1494,7 +1537,7 @@ static struct platform_driver fsl_xcvr_driver = { .probe = fsl_xcvr_probe, .driver = { .name = "fsl,imx8mp-audio-xcvr", - .pm = &fsl_xcvr_pm_ops, + .pm = pm_ptr(&fsl_xcvr_pm_ops), .of_match_table = fsl_xcvr_dt_ids, }, .remove_new = fsl_xcvr_remove, diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h index 044058fc6aa2..882428592e1a 100644 --- a/sound/soc/fsl/fsl_xcvr.h +++ b/sound/soc/fsl/fsl_xcvr.h @@ -291,4 +291,95 @@ #define FSL_XCVR_RX_CS_BUFF_1 0xA0 /* Second RX CS buffer */ #define FSL_XCVR_CAP_DATA_STR 0x300 /* Capabilities data structure */ +/* GP PLL Registers */ +#define FSL_XCVR_GP_PLL_CTRL 0x00 +#define FSL_XCVR_GP_PLL_CTRL_SET 0x04 +#define FSL_XCVR_GP_PLL_CTRL_CLR 0x08 +#define FSL_XCVR_GP_PLL_CTRL_TOG 0x0C +#define FSL_XCVR_GP_PLL_ANA_PRG 0x10 +#define FSL_XCVR_GP_PLL_ANA_PRG_SET 0x14 +#define FSL_XCVR_GP_PLL_ANA_PRG_CLR 0x18 +#define FSL_XCVR_GP_PLL_ANA_PRG_TOG 0x1C +#define FSL_XCVR_GP_PLL_TEST 0x20 +#define FSL_XCVR_GP_PLL_TEST_SET 0x24 +#define FSL_XCVR_GP_PLL_TEST_CLR 0x28 +#define FSL_XCVR_GP_PLL_TEST_TOG 0x2C +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM 0x30 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_SET 0x34 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_CLR 0x38 +#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_TOG 0x3C +#define FSL_XCVR_GP_PLL_NUMERATOR 0x40 +#define FSL_XCVR_GP_PLL_NUMERATOR_SET 0x44 +#define FSL_XCVR_GP_PLL_NUMERATOR_CLR 0x48 +#define FSL_XCVR_GP_PLL_NUMERATOR_TOG 0x4C +#define FSL_XCVR_GP_PLL_DENOMINATOR 0x50 +#define FSL_XCVR_GP_PLL_DENOMINATOR_SET 0x54 +#define FSL_XCVR_GP_PLL_DENOMINATOR_CLR 0x58 +#define FSL_XCVR_GP_PLL_DENOMINATOR_TOG 0x5C +#define FSL_XCVR_GP_PLL_DIV 0x60 +#define FSL_XCVR_GP_PLL_DIV_SET 0x64 +#define FSL_XCVR_GP_PLL_DIV_CLR 0x68 +#define FSL_XCVR_GP_PLL_DIV_TOG 0x6C +#define FSL_XCVR_GP_PLL_DFS_CTRL0 0x70 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_SET 0x74 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_CLR 0x78 +#define FSL_XCVR_GP_PLL_DFS_CTRL0_TOG 0x7C +#define FSL_XCVR_GP_PLL_DFS_DIV0 0x80 +#define FSL_XCVR_GP_PLL_DFS_DIV0_SET 0x84 +#define FSL_XCVR_GP_PLL_DFS_DIV0_CLR 0x88 +#define FSL_XCVR_GP_PLL_DFS_DIV0_TOG 0x8C +#define FSL_XCVR_GP_PLL_DFS_CTRL1 0x90 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_SET 0x94 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_CLR 0x98 +#define FSL_XCVR_GP_PLL_DFS_CTRL1_TOG 0x9C +#define FSL_XCVR_GP_PLL_DFS_DIV1 0xA0 +#define FSL_XCVR_GP_PLL_DFS_DIV1_SET 0xA4 +#define FSL_XCVR_GP_PLL_DFS_DIV1_CLR 0xA8 +#define FSL_XCVR_GP_PLL_DFS_DIV1_TOG 0xAC +#define FSL_XCVR_GP_PLL_DFS_CTRL2 0xB0 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_SET 0xB4 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_CLR 0xB8 +#define FSL_XCVR_GP_PLL_DFS_CTRL2_TOG 0xBC +#define FSL_XCVR_GP_PLL_DFS_DIV2 0xC0 +#define FSL_XCVR_GP_PLL_DFS_DIV2_SET 0xC4 +#define FSL_XCVR_GP_PLL_DFS_DIV2_CLR 0xC8 +#define FSL_XCVR_GP_PLL_DFS_DIV2_TOG 0xCC +#define FSL_XCVR_GP_PLL_DFS_CTRL3 0xD0 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_SET 0xD4 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_CLR 0xD8 +#define FSL_XCVR_GP_PLL_DFS_CTRL3_TOG 0xDC +#define FSL_XCVR_GP_PLL_DFS_DIV3 0xE0 +#define FSL_XCVR_GP_PLL_DFS_DIV3_SET 0xE4 +#define FSL_XCVR_GP_PLL_DFS_DIV3_CLR 0xE8 +#define FSL_XCVR_GP_PLL_DFS_DIV3_TOG 0xEC +#define FSL_XCVR_GP_PLL_STATUS 0xF0 +#define FSL_XCVR_GP_PLL_STATUS_SET 0xF4 +#define FSL_XCVR_GP_PLL_STATUS_CLR 0xF8 +#define FSL_XCVR_GP_PLL_STATUS_TOG 0xFC + +/* GP PLL Control Register */ +#define FSL_XCVR_GP_PLL_CTRL_LBYPASS BIT(31) +#define FSL_XCVR_GP_PLL_CTRL_HCS BIT(16) +#define FSL_XCVR_GP_PLL_CTRL_MSD BIT(12) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN3 BIT(11) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN2 BIT(10) +#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN1 BIT(9) +#define FSL_XCVR_GP_PLL_CTRL_SPREADCTL BIT(8) +#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_BYPASS BIT(2) +#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN BIT(1) +#define FSL_XCVR_GP_PLL_CTRL_POWERUP BIT(0) + +/* GP PLL Numerator Register */ +#define FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT 2 +#define FSL_XCVR_GP_PLL_NUMERATOR_MFN GENMASK(31, 2) + +/* GP PLL Denominator Register */ +#define FSL_XCVR_GP_PLL_DENOMINATOR_MFD GENMASK(29, 0) + +/* GP PLL Dividers Register */ +#define FSL_XCVR_GP_PLL_DIV_MFI_SHIFT 16 +#define FSL_XCVR_GP_PLL_DIV_MFI GENMASK(24, 16) +#define FSL_XCVR_GP_PLL_DIV_RDIV GENMASK(15, 13) +#define FSL_XCVR_GP_PLL_DIV_ODIV GENMASK(7, 0) + #endif /* __FSL_XCVR_H */ diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 2aeb18397bcb..6fbcf33fd0de 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -140,6 +140,13 @@ static const struct snd_soc_ops imx_audmix_be_ops = { .hw_params = imx_audmix_be_hw_params, }; +static const char *name[][3] = { + {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"}, + {"sai-tx", "sai-tx", "sai-rx"}, + {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"}, + {"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"}, +}; + static int imx_audmix_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -150,7 +157,7 @@ static int imx_audmix_probe(struct platform_device *pdev) struct imx_audmix *priv; int i, num_dai, ret; const char *fe_name_pref = "HiFi-AUDMIX-FE-"; - char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name; + char *be_name, *dai_name; if (pdev->dev.parent) { audmix_np = pdev->dev.parent->of_node; @@ -183,6 +190,7 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + num_dai += 1; priv->num_dai = 2 * num_dai; priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai, sizeof(struct snd_soc_dai_link), GFP_KERNEL); @@ -196,7 +204,7 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!priv->dai_conf) return -ENOMEM; - priv->num_dapm_routes = 3 * num_dai; + priv->num_dapm_routes = num_dai; priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes, sizeof(struct snd_soc_dapm_route), GFP_KERNEL); @@ -211,8 +219,12 @@ static int imx_audmix_probe(struct platform_device *pdev) if (!dlc) return -ENOMEM; - ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i, - &args); + if (i == num_dai - 1) + ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, 0, + &args); + else + ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i, + &args); if (ret < 0) { dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n"); return ret; @@ -226,20 +238,14 @@ static int imx_audmix_probe(struct platform_device *pdev) put_device(&cpu_pdev->dev); dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s", - fe_name_pref, args.np->full_name + 1); + fe_name_pref, args.np->full_name); if (!dai_name) return -ENOMEM; dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name); - if (i == 0) { + if (i == num_dai - 1) out_cpu_np = args.np; - capture_dai_name = - devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", - dai_name, "CPU-Capture"); - if (!capture_dai_name) - return -ENOMEM; - } /* * CPU == Platform @@ -252,27 +258,23 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[i].num_cpus = 1; priv->dai[i].num_codecs = 1; priv->dai[i].num_platforms = 1; - - priv->dai[i].name = dai_name; + priv->dai[i].name = name[0][i]; priv->dai[i].stream_name = "HiFi-AUDMIX-FE"; priv->dai[i].cpus->of_node = args.np; - priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev); + priv->dai[i].cpus->dai_name = name[1][i]; + priv->dai[i].dynamic = 1; priv->dai[i].dpcm_playback = 1; - priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0); + if (i == num_dai - 1) { + priv->dai[i].dpcm_capture = 1; + priv->dai[i].dpcm_playback = 0; + } priv->dai[i].ignore_pmdown_time = 1; priv->dai[i].ops = &imx_audmix_fe_ops; /* Add AUDMIX Backend */ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "audmix-%d", i); - be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "AUDMIX-Playback-%d", i); - be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "AUDMIX-Capture-%d", i); - if (!be_name || !be_pb || !be_cp) - return -ENOMEM; - priv->dai[num_dai + i].cpus = &dlc[1]; priv->dai[num_dai + i].codecs = &snd_soc_dummy_dlc; @@ -284,24 +286,33 @@ static int imx_audmix_probe(struct platform_device *pdev) priv->dai[num_dai + i].cpus->dai_name = be_name; priv->dai[num_dai + i].no_pcm = 1; priv->dai[num_dai + i].dpcm_playback = 1; - priv->dai[num_dai + i].dpcm_capture = 1; + if (i == num_dai - 1) { + priv->dai[num_dai + i].dpcm_capture = 1; + priv->dai[num_dai + i].dpcm_playback = 0; + } priv->dai[num_dai + i].ignore_pmdown_time = 1; priv->dai[num_dai + i].ops = &imx_audmix_be_ops; priv->dai_conf[i].dlc.of_node = args.np; priv->dai_conf[i].name_prefix = dai_name; - priv->dapm_routes[i].source = - devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", - dai_name, "CPU-Playback"); - if (!priv->dapm_routes[i].source) - return -ENOMEM; + if (i == num_dai - 1) { + priv->dapm_routes[i].sink = + devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", + dai_name, name[2][i]); + if (!priv->dapm_routes[i].sink) + return -ENOMEM; - priv->dapm_routes[i].sink = be_pb; - priv->dapm_routes[num_dai + i].source = be_pb; - priv->dapm_routes[num_dai + i].sink = be_cp; - priv->dapm_routes[2 * num_dai + i].source = be_cp; - priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name; + priv->dapm_routes[i].source = name[3][i]; + } else { + priv->dapm_routes[i].source = + devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", + dai_name, name[3][i]); + if (!priv->dapm_routes[i].source) + return -ENOMEM; + + priv->dapm_routes[i].sink = name[2][i]; + } } cpu_pdev = of_find_device_by_node(out_cpu_np); diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 5b9648f3b087..3ef92f6dfc6b 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -8,7 +8,6 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/i2c.h> -#include <linux/of_gpio.h> #include <sound/soc.h> #include <sound/jack.h> diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 0d124002678e..3391430e4253 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -319,4 +319,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit); +MODULE_DESCRIPTION("Freescale i.MX PCM FIQ handler"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 0f1ad7ad7d27..ce98d2288193 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -5,9 +5,7 @@ #include <linux/of_platform.h> #include <linux/of_reserved_mem.h> #include <linux/i2c.h> -#include <linux/of_gpio.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/clk.h> #include <sound/soc.h> #include <sound/jack.h> diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c deleted file mode 100644 index 1e57939a7e29..000000000000 --- a/sound/soc/fsl/imx-spdif.c +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// Copyright (C) 2013 Freescale Semiconductor, Inc. - -#include <linux/module.h> -#include <linux/of_platform.h> -#include <sound/soc.h> - -struct imx_spdif_data { - struct snd_soc_dai_link dai; - struct snd_soc_card card; -}; - -static int imx_spdif_audio_probe(struct platform_device *pdev) -{ - struct device_node *spdif_np, *np = pdev->dev.of_node; - struct imx_spdif_data *data; - struct snd_soc_dai_link_component *comp; - int ret = 0; - - spdif_np = of_parse_phandle(np, "spdif-controller", 0); - if (!spdif_np) { - dev_err(&pdev->dev, "failed to find spdif-controller\n"); - ret = -EINVAL; - goto end; - } - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL); - if (!data || !comp) { - ret = -ENOMEM; - goto end; - } - - /* - * CPU == Platform - * platform is using soc-generic-dmaengine-pcm - */ - data->dai.cpus = - data->dai.platforms = comp; - data->dai.codecs = &snd_soc_dummy_dlc; - - data->dai.num_cpus = 1; - data->dai.num_codecs = 1; - data->dai.num_platforms = 1; - - data->dai.name = "S/PDIF PCM"; - data->dai.stream_name = "S/PDIF PCM"; - data->dai.cpus->of_node = spdif_np; - data->dai.playback_only = true; - data->dai.capture_only = true; - - if (of_property_read_bool(np, "spdif-out")) - data->dai.capture_only = false; - - if (of_property_read_bool(np, "spdif-in")) - data->dai.playback_only = false; - - if (data->dai.playback_only && data->dai.capture_only) { - dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); - goto end; - } - - data->card.dev = &pdev->dev; - data->card.dai_link = &data->dai; - data->card.num_links = 1; - data->card.owner = THIS_MODULE; - - ret = snd_soc_of_parse_card_name(&data->card, "model"); - if (ret) - goto end; - - ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) - dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); - -end: - of_node_put(spdif_np); - - return ret; -} - -static const struct of_device_id imx_spdif_dt_ids[] = { - { .compatible = "fsl,imx-audio-spdif", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); - -static struct platform_driver imx_spdif_driver = { - .driver = { - .name = "imx-spdif", - .pm = &snd_soc_pm_ops, - .of_match_table = imx_spdif_dt_ids, - }, - .probe = imx_spdif_audio_probe, -}; - -module_platform_driver(imx_spdif_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:imx-spdif"); diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c new file mode 100644 index 000000000000..af995ca081a3 --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells <kevin.wells@nxp.com> +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com> + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "lpc3xxx-i2s.h" + +#define I2S_PLAYBACK_FLAG 0x1 +#define I2S_CAPTURE_FLAG 0x2 + +#define LPC3XXX_I2S_RATES ( \ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + +#define LPC3XXX_I2S_FORMATS ( \ + SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate) +{ + u32 i2srate; + u32 idxx, idyy; + u32 savedbitclkrate, diff, trate, baseclk; + + /* Adjust rate for sample size (bits) and 2 channels and offset for + * divider in clock output + */ + i2srate = (freq / 100) * 2 * (8 * xbytes); + i2srate = i2srate << 1; + clkrate = clkrate / 100; + baseclk = clkrate; + *clkx = 1; + *clky = 1; + + /* Find the best divider */ + *clkx = *clky = 0; + savedbitclkrate = 0; + diff = ~0; + for (idxx = 1; idxx < 0xFF; idxx++) { + for (idyy = 1; idyy < 0xFF; idyy++) { + trate = (baseclk * idxx) / idyy; + if (abs(trate - i2srate) < diff) { + diff = abs(trate - i2srate); + savedbitclkrate = trate; + *clkx = idxx; + *clky = idyy; + } + } + } +} + +static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + u32 flag; + int ret = 0; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + flag = I2S_PLAYBACK_FLAG; + else + flag = I2S_CAPTURE_FLAG; + + if (flag & i2s_info_p->streams_in_use) { + dev_warn(dev, "I2S channel is busy\n"); + ret = -EBUSY; + return ret; + } + + if (i2s_info_p->streams_in_use == 0) { + ret = clk_prepare_enable(i2s_info_p->clk); + if (ret) { + dev_err(dev, "Can't enable clock, err=%d\n", ret); + return ret; + } + } + + i2s_info_p->streams_in_use |= flag; + return 0; +} + +static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP); + u32 flag; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + flag = I2S_PLAYBACK_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits); + } else { + flag = I2S_CAPTURE_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits); + } + i2s_info_p->streams_in_use &= ~flag; + + if (i2s_info_p->streams_in_use == 0) + clk_disable_unprepare(i2s_info_p->clk); +} + +static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + + /* Will use in HW params later */ + i2s_info_p->freq = freq; + + return 0; +} + +static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { + dev_warn(dev, "unsupported bus format %d\n", fmt); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + dev_warn(dev, "unsupported clock provider %d\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + struct regmap *regs = i2s_info_p->regs; + int xfersize; + u32 tmp, clkx, clky; + + tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP); + xfersize = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP); + xfersize = 2; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP); + xfersize = 4; + break; + + default: + dev_warn(dev, "Unsupported audio data format %d\n", params_format(params)); + return -EINVAL; + } + + if (params_channels(params) == 1) + tmp |= LPC3XXX_I2S_MONO; + + __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate); + + dev_dbg(dev, "Stream : %s\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq); + dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate); + dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize); + dev_dbg(dev, "Clock divider (x) : %d\n", clkx); + dev_dbg(dev, "Clock divider (y) : %d\n", clky); + dev_dbg(dev, "Channels : %d\n", params_channels(params)); + dev_dbg(dev, "Data format : %s\n", "I2S"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(regs, LPC3XXX_REG_I2S_DMA1, + LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp); + } else { + regmap_write(regs, LPC3XXX_REG_I2S_DMA0, + LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp); + } + + return 0; +} + +static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + break; + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config, + &i2s_info_p->capture_dma_config); + return 0; +} + +const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = { + .probe = lpc3xxx_i2s_dai_probe, + .startup = lpc3xxx_i2s_startup, + .shutdown = lpc3xxx_i2s_shutdown, + .trigger = lpc3xxx_i2s_trigger, + .hw_params = lpc3xxx_i2s_hw_params, + .set_sysclk = lpc3xxx_i2s_set_dai_sysclk, + .set_fmt = lpc3xxx_i2s_set_dai_fmt, +}; + +struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = { + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .ops = &lpc3xxx_i2s_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + +static const struct snd_soc_component_driver lpc32xx_i2s_component = { + .name = "lpc32xx-i2s", + .legacy_dai_naming = 1, +}; + +static const struct regmap_config lpc32xx_i2s_regconfig = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPC3XXX_REG_I2S_RX_RATE, +}; + +static int lpc32xx_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpc3xxx_i2s_info *i2s_info_p; + struct resource *res; + void __iomem *iomem; + int ret; + + i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL); + if (!i2s_info_p) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s_info_p); + i2s_info_p->dev = dev; + + iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(iomem)) + return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n"); + + i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig); + if (IS_ERR(i2s_info_p->regs)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs), + "failed to init register map: %pe\n", i2s_info_p->regs); + + i2s_info_p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(i2s_info_p->clk)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n"); + + i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk); + if (i2s_info_p->clkrate == 0) + return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n"); + + mutex_init(&i2s_info_p->lock); + + ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component, + &lpc3xxx_i2s_dai_driver, 1); + if (ret) + return dev_err_probe(dev, ret, "Can't register cpu_dai component\n"); + + i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO); + i2s_info_p->playback_dma_config.maxburst = 4; + + i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO); + i2s_info_p->capture_dma_config.maxburst = 4; + + ret = lpc3xxx_pcm_register(pdev); + if (ret) + return dev_err_probe(dev, ret, "Can't register pcm component\n"); + + return 0; +} + +static const struct of_device_id lpc32xx_i2s_match[] = { + { .compatible = "nxp,lpc3220-i2s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match); + +static struct platform_driver lpc32xx_i2s_driver = { + .probe = lpc32xx_i2s_probe, + .driver = { + .name = "lpc3xxx-i2s", + .of_match_table = lpc32xx_i2s_match, + }, +}; + +module_platform_driver(lpc32xx_i2s_driver); + +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_AUTHOR("Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>"); +MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h new file mode 100644 index 000000000000..b6657853017a --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Author: Kevin Wells <kevin.wells@nxp.com> + * + * Copyright (C) 2008 NXP Semiconductors + * Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com> + */ + +#ifndef __SOUND_SOC_LPC3XXX_I2S_H +#define __SOUND_SOC_LPC3XXX_I2S_H + +#include <linux/bitfield.h> +#include <linux/types.h> +#include <linux/regmap.h> + +struct lpc3xxx_i2s_info { + struct device *dev; + struct clk *clk; + struct mutex lock; /* To serialize user-space access */ + struct regmap *regs; + u32 streams_in_use; + u32 clkrate; + int freq; + struct snd_dmaengine_dai_dma_data playback_dma_config; + struct snd_dmaengine_dai_dma_data capture_dma_config; +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev); + +/* I2S controller register offsets */ +#define LPC3XXX_REG_I2S_DAO 0x00 +#define LPC3XXX_REG_I2S_DAI 0x04 +#define LPC3XXX_REG_I2S_TX_FIFO 0x08 +#define LPC3XXX_REG_I2S_RX_FIFO 0x0C +#define LPC3XXX_REG_I2S_STAT 0x10 +#define LPC3XXX_REG_I2S_DMA0 0x14 +#define LPC3XXX_REG_I2S_DMA1 0x18 +#define LPC3XXX_REG_I2S_IRQ 0x1C +#define LPC3XXX_REG_I2S_TX_RATE 0x20 +#define LPC3XXX_REG_I2S_RX_RATE 0x24 + +/* i2s_daO i2s_dai register definitions */ +#define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */ +#define LPC3XXX_I2S_WW16 FIELD_PREP(0x3, 1) /* Word width is 16bit */ +#define LPC3XXX_I2S_WW32 FIELD_PREP(0x3, 3) /* Word width is 32bit */ +#define LPC3XXX_I2S_MONO BIT(2) /* Mono */ +#define LPC3XXX_I2S_STOP BIT(3) /* Stop, diables the access to FIFO, mutes the channel */ +#define LPC3XXX_I2S_RESET BIT(4) /* Reset the channel */ +#define LPC3XXX_I2S_WS_SEL BIT(5) /* Channel Master(0) or slave(1) mode select */ +#define LPC3XXX_I2S_WS_HP(s) FIELD_PREP(0x7FC0, s) /* Word select half period - 1 */ +#define LPC3XXX_I2S_MUTE BIT(15) /* Mute the channel, Transmit channel only */ + +#define LPC3XXX_I2S_WW32_HP 0x1f /* Word select half period for 32bit word width */ +#define LPC3XXX_I2S_WW16_HP 0x0f /* Word select half period for 16bit word width */ +#define LPC3XXX_I2S_WW8_HP 0x7 /* Word select half period for 8bit word width */ + +/* i2s_stat register definitions */ +#define LPC3XXX_I2S_IRQ_STAT BIT(0) +#define LPC3XXX_I2S_DMA0_REQ BIT(1) +#define LPC3XXX_I2S_DMA1_REQ BIT(2) + +/* i2s_dma0 Configuration register definitions */ +#define LPC3XXX_I2S_DMA0_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA0_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA0_RX_DEPTH(s) FIELD_PREP(0xF00, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */ + +/* i2s_dma1 Configuration register definitions */ +#define LPC3XXX_I2S_DMA1_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA1_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA1_TX_DEPTH(s) FIELD_PREP(0x70000, s) /* Set the DMA1 TX Request level */ + +/* i2s_irq register definitions */ +#define LPC3XXX_I2S_RX_IRQ_EN BIT(0) /* Enable RX IRQ */ +#define LPC3XXX_I2S_TX_IRQ_EN BIT(1) /* Enable TX IRQ */ +#define LPC3XXX_I2S_IRQ_RX_DEPTH(s) FIELD_PREP(0xFF00, s) /* valid values ar 0 to 7 */ +#define LPC3XXX_I2S_IRQ_TX_DEPTH(s) FIELD_PREP(0xFF0000, s) /* valid values ar 0 to 7 */ + +#endif diff --git a/sound/soc/fsl/lpc3xxx-pcm.c b/sound/soc/fsl/lpc3xxx-pcm.c new file mode 100644 index 000000000000..c0d499b9b8ba --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-pcm.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells <kevin.wells@nxp.com> +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amba/pl08x.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> +#include <sound/soc.h> + +#include "lpc3xxx-i2s.h" + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static const struct snd_pcm_hardware lpc3xxx_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = STUB_FORMATS, + .period_bytes_min = 128, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024 +}; + +static const struct snd_dmaengine_pcm_config lpc3xxx_dmaengine_pcm_config = { + .pcm_hardware = &lpc3xxx_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = pl08x_filter_id, + .prealloc_buffer_size = 128 * 1024, +}; + +const struct snd_soc_component_driver lpc3xxx_soc_platform_driver = { + .name = "lpc32xx-pcm", +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev) +{ + int ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &lpc3xxx_dmaengine_pcm_config, 0); + if (ret) { + dev_err(&pdev->dev, "failed to register dmaengine: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, &lpc3xxx_soc_platform_driver, + NULL, 0); +} +EXPORT_SYMBOL(lpc3xxx_pcm_register); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 83e3ba773fbd..3425fbbcbd7e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -7,6 +7,7 @@ // // based on ${LINUX}/sound/soc/generic/simple-card.c +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio/consumer.h> @@ -19,6 +20,18 @@ #define DPCM_SELECTABLE 1 +#define ep_to_port(ep) of_get_parent(ep) +static struct device_node *port_to_ports(struct device_node *port) +{ + struct device_node *ports = of_get_parent(port); + + if (!of_node_name_eq(ports, "ports")) { + of_node_put(ports); + return NULL; + } + return ports; +} + static int graph_outdrv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -68,13 +81,12 @@ static void graph_parse_convert(struct device *dev, struct simple_util_data *adata) { struct device_node *top = dev->of_node; - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); struct device_node *node = of_graph_get_port_parent(ep); simple_util_parse_convert(top, NULL, adata); - if (of_node_name_eq(ports, "ports")) - simple_util_parse_convert(ports, NULL, adata); + simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); @@ -83,30 +95,12 @@ static void graph_parse_convert(struct device *dev, of_node_put(node); } -static void graph_parse_mclk_fs(struct device_node *top, - struct device_node *ep, - struct simple_dai_props *props) -{ - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); - - of_property_read_u32(top, "mclk-fs", &props->mclk_fs); - if (of_node_name_eq(ports, "ports")) - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); - of_property_read_u32(port, "mclk-fs", &props->mclk_fs); - of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); - - of_node_put(port); - of_node_put(ports); -} - static int graph_parse_node(struct simple_util_priv *priv, struct device_node *ep, struct link_info *li, int *cpu) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct snd_soc_dai_link_component *dlc; @@ -121,8 +115,6 @@ static int graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - graph_parse_mclk_fs(top, ep, dai_props); - ret = graph_util_parse_dai(dev, ep, dlc, cpu); if (ret < 0) return ret; @@ -139,26 +131,70 @@ static int graph_parse_node(struct simple_util_priv *priv, } static int graph_link_init(struct simple_util_priv *priv, - struct device_node *cpu_ep, - struct device_node *codec_ep, + struct device_node *ep_cpu, + struct device_node *ep_codec, struct link_info *li, char *name) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct device_node *port_cpu = ep_to_port(ep_cpu); + struct device_node *port_codec = ep_to_port(ep_codec); + struct device_node *ports_cpu = port_to_ports(port_cpu); + struct device_node *ports_codec = port_to_ports(port_codec); + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; + bool playback_only = 0, capture_only = 0; int ret; - ret = simple_util_parse_daifmt(dev, cpu_ep, codec_ep, + ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec, NULL, &dai_link->dai_fmt); if (ret < 0) - return ret; + goto init_end; + + graph_util_parse_link_direction(top, &playback_only, &capture_only); + graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(port_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); + + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + + graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop); + + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; + + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) dai_link->ops = priv->ops; - return simple_util_set_dailink_name(dev, dai_link, name); + ret = simple_util_set_dailink_name(dev, dai_link, name); +init_end: + of_node_put(ports_cpu); + of_node_put(ports_codec); + of_node_put(port_cpu); + of_node_put(port_codec); + + return ret; } static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, @@ -231,14 +267,11 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, "be.%pOFP.%s", codecs->of_node, codecs->dai_name); /* check "prefix" from top node */ - port = of_get_parent(ep); - ports = of_get_parent(port); - snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, - "prefix"); - if (of_node_name_eq(ports, "ports")) - snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix"); - snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, - "prefix"); + port = ep_to_port(ep); + ports = port_to_ports(port); + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, "prefix"); of_node_put(ports); of_node_put(port); @@ -350,7 +383,7 @@ static int __graph_for_each_link(struct simple_util_priv *priv, /* get codec */ codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_get_parent(codec_ep); + codec_port = ep_to_port(codec_ep); /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); @@ -541,10 +574,9 @@ static int graph_get_dais_count(struct simple_util_priv *priv, int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) { struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; int ret; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -596,7 +628,6 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; - devm_kfree(dev, li); return 0; err: diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.c b/sound/soc/generic/audio-graph-card2-custom-sample.c index 1b6ccd2de964..8e5a51098490 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.c +++ b/sound/soc/generic/audio-graph-card2-custom-sample.c @@ -5,8 +5,9 @@ // Copyright (C) 2020 Renesas Electronics Corp. // Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> // +#include <linux/device.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <sound/graph_card.h> diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 81e84095107e..56f7f946882e 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -236,6 +236,18 @@ enum graph_type { #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") +#define ep_to_port(ep) of_get_parent(ep) +static struct device_node *port_to_ports(struct device_node *port) +{ + struct device_node *ports = of_get_parent(port); + + if (!of_node_name_eq(ports, "ports")) { + of_node_put(ports); + return NULL; + } + return ports; +} + static enum graph_type __graph_get_type(struct device_node *lnk) { struct device_node *np, *parent_np; @@ -320,7 +332,7 @@ static int graph_lnk_is_multi(struct device_node *lnk) static struct device_node *graph_get_next_multi_ep(struct device_node **port) { - struct device_node *ports = of_get_parent(*port); + struct device_node *ports = port_to_ports(*port); struct device_node *ep = NULL; struct device_node *rep = NULL; @@ -365,12 +377,11 @@ static const struct snd_soc_ops graph_ops = { static void graph_parse_convert(struct device_node *ep, struct simple_dai_props *props) { - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); + struct device_node *port = ep_to_port(ep); + struct device_node *ports = port_to_ports(port); struct simple_util_data *adata = &props->adata; - if (of_node_name_eq(ports, "ports")) - simple_util_parse_convert(ports, NULL, adata); + simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); @@ -378,21 +389,6 @@ static void graph_parse_convert(struct device_node *ep, of_node_put(ports); } -static void graph_parse_mclk_fs(struct device_node *ep, - struct simple_dai_props *props) -{ - struct device_node *port = of_get_parent(ep); - struct device_node *ports = of_get_parent(port); - - if (of_node_name_eq(ports, "ports")) - of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); - of_property_read_u32(port, "mclk-fs", &props->mclk_fs); - of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); - - of_node_put(port); - of_node_put(ports); -} - static int __graph_parse_node(struct simple_util_priv *priv, enum graph_type gtype, struct device_node *ep, @@ -414,8 +410,6 @@ static int __graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, idx); } - graph_parse_mclk_fs(ep, dai_props); - ret = graph_util_parse_dai(dev, ep, dlc, &is_single_links); if (ret < 0) return ret; @@ -481,11 +475,10 @@ static int __graph_parse_node(struct simple_util_priv *priv, if (!is_cpu && gtype == GRAPH_DPCM) { struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx); struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); - struct device_node *rport = of_get_parent(ep); - struct device_node *rports = of_get_parent(rport); + struct device_node *rport = ep_to_port(ep); + struct device_node *rports = port_to_ports(rport); - if (of_node_name_eq(rports, "ports")) - snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); + snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); of_node_put(rport); @@ -539,11 +532,11 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, */ struct device_node *mcpu_ep = port_to_endpoint(mcpu_port); struct device_node *mcpu_ep_n = mcpu_ep; - struct device_node *mcpu_port_top = of_get_next_child(of_get_parent(mcpu_port), NULL); + struct device_node *mcpu_port_top = of_get_next_child(port_to_ports(mcpu_port), NULL); struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top); struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top); - struct device_node *mcodec_port_top = of_get_parent(mcodec_ep_top); - struct device_node *mcodec_ports = of_get_parent(mcodec_port_top); + struct device_node *mcodec_port_top = ep_to_port(mcodec_ep_top); + struct device_node *mcodec_ports = port_to_ports(mcodec_port_top); int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); int ret = -EINVAL; @@ -566,9 +559,9 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, } mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n); - mcodec_port = of_get_parent(mcodec_ep_n); + mcodec_port = ep_to_port(mcodec_ep_n); - if (mcodec_ports != of_get_parent(mcodec_port)) + if (mcodec_ports != port_to_ports(mcodec_port)) goto mcpu_err; codec_idx = 0; @@ -705,6 +698,9 @@ static void graph_parse_daifmt(struct device_node *node, { unsigned int fmt; + if (!node) + return; + /* * see also above "daifmt" explanation * and samples. @@ -751,43 +747,74 @@ static void graph_parse_daifmt(struct device_node *node, } static void graph_link_init(struct simple_util_priv *priv, - struct device_node *port, + struct device_node *lnk, + struct device_node *port_cpu, + struct device_node *port_codec, struct link_info *li, int is_cpu_node) { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); - struct device_node *ep; - struct device_node *ports; + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct device_node *ep_cpu, *ep_codec; + struct device_node *ports_cpu, *ports_codec; unsigned int daifmt = 0, daiclk = 0; bool playback_only = 0, capture_only = 0; + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; unsigned int bit_frame = 0; - if (graph_lnk_is_multi(port)) { - of_node_get(port); - ep = graph_get_next_multi_ep(&port); - port = of_get_parent(ep); + of_node_get(port_cpu); + if (graph_lnk_is_multi(port_cpu)) { + ep_cpu = graph_get_next_multi_ep(&port_cpu); + of_node_put(port_cpu); + port_cpu = ep_to_port(ep_cpu); } else { - ep = port_to_endpoint(port); + ep_cpu = port_to_endpoint(port_cpu); } + ports_cpu = port_to_ports(port_cpu); - ports = of_get_parent(port); - - /* - * ports { - * (A) - * port { - * (B) - * endpoint { - * (C) - * }; - * }; - * }; - * }; - */ - graph_parse_daifmt(ep, &daifmt, &bit_frame); /* (C) */ - graph_parse_daifmt(port, &daifmt, &bit_frame); /* (B) */ - if (of_node_name_eq(ports, "ports")) - graph_parse_daifmt(ports, &daifmt, &bit_frame); /* (A) */ + of_node_get(port_codec); + if (graph_lnk_is_multi(port_codec)) { + ep_codec = graph_get_next_multi_ep(&port_codec); + of_node_put(port_cpu); + port_codec = ep_to_port(ep_codec); + } else { + ep_codec = port_to_endpoint(port_codec); + } + ports_codec = port_to_ports(port_codec); + + + graph_parse_daifmt(ep_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(ep_codec, &daifmt, &bit_frame); + graph_parse_daifmt(port_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(port_codec, &daifmt, &bit_frame); + graph_parse_daifmt(ports_cpu, &daifmt, &bit_frame); + graph_parse_daifmt(ports_codec, &daifmt, &bit_frame); + graph_parse_daifmt(lnk, &daifmt, &bit_frame); + + graph_util_parse_link_direction(lnk, &playback_only, &capture_only); + graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ports_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(port_codec, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only); + + of_property_read_u32(lnk, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs); + + graph_util_parse_trigger_order(priv, lnk, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop); /* * convert bit_frame @@ -798,16 +825,24 @@ static void graph_link_init(struct simple_util_priv *priv, if (is_cpu_node) daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); - graph_util_parse_link_direction(port, &playback_only, &capture_only); + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; - dai_link->playback_only = playback_only; - dai_link->capture_only = capture_only; + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; dai_link->dai_fmt = daifmt | daiclk; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) dai_link->ops = priv->ops; + + of_node_put(ports_cpu); + of_node_put(ports_codec); + of_node_put(port_cpu); + of_node_put(port_codec); + of_node_put(ep_cpu); + of_node_put(ep_codec); } int audio_graph2_link_normal(struct simple_util_priv *priv, @@ -835,7 +870,7 @@ int audio_graph2_link_normal(struct simple_util_priv *priv, if (ret < 0) goto err; - graph_link_init(priv, cpu_port, li, 1); + graph_link_init(priv, lnk, cpu_port, codec_port, li, 1); err: of_node_put(codec_port); of_node_put(cpu_ep); @@ -850,13 +885,16 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, { struct device_node *ep = port_to_endpoint(lnk); struct device_node *rep = of_graph_get_remote_endpoint(ep); - struct device_node *rport = of_graph_get_remote_port(ep); + struct device_node *cpu_port = NULL; + struct device_node *codec_port = NULL; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); int is_cpu = graph_util_is_ports0(lnk); int ret; if (is_cpu) { + cpu_port = of_graph_get_remote_port(ep); /* rport */ + /* * dpcm { * // Front-End @@ -884,10 +922,13 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; - ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1); + ret = graph_parse_node(priv, GRAPH_DPCM, cpu_port, li, 1); if (ret) goto err; + } else { + codec_port = of_graph_get_remote_port(ep); /* rport */ + /* * dpcm { * // Front-End @@ -917,7 +958,7 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup; - ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0); + ret = graph_parse_node(priv, GRAPH_DPCM, codec_port, li, 0); if (ret < 0) goto err; } @@ -927,11 +968,12 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, snd_soc_dai_link_set_capabilities(dai_link); - graph_link_init(priv, rport, li, is_cpu); + graph_link_init(priv, lnk, cpu_port, codec_port, li, is_cpu); err: of_node_put(ep); of_node_put(rep); - of_node_put(rport); + of_node_put(cpu_port); + of_node_put(codec_port); return ret; } @@ -966,7 +1008,7 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, */ of_node_get(lnk); port0 = lnk; - ports = of_get_parent(port0); + ports = port_to_ports(port0); port1 = of_get_next_child(ports, lnk); /* @@ -1019,7 +1061,7 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, if (ret < 0) goto err2; - graph_link_init(priv, codec0_port, li, 1); + graph_link_init(priv, lnk, codec0_port, codec1_port, li, 1); err2: of_node_put(ep0); of_node_put(ep1); @@ -1098,7 +1140,7 @@ static int graph_counter(struct device_node *lnk) * ignore first lnk part */ if (graph_lnk_is_multi(lnk)) { - struct device_node *ports = of_get_parent(lnk); + struct device_node *ports = port_to_ports(lnk); struct device_node *port = NULL; int cnt = 0; @@ -1195,7 +1237,7 @@ static int graph_count_c2c(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { - struct device_node *ports = of_get_parent(lnk); + struct device_node *ports = port_to_ports(lnk); struct device_node *port0 = lnk; struct device_node *port1 = of_get_next_child(ports, of_node_get(lnk)); struct device_node *ep0 = port_to_endpoint(port0); @@ -1308,10 +1350,9 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, struct graph2_custom_hooks *hooks) { struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; int ret; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -1369,10 +1410,12 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret < 0) + goto err; + ret = devm_snd_soc_register_card(dev, card); err: - devm_kfree(dev, li); - if (ret < 0) dev_err_probe(dev, ret, "parse error\n"); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index b4876b4f259d..fedae7f6f70c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -4,6 +4,8 @@ // // Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +#include <dt-bindings/sound/audio-graph.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/module.h> @@ -13,12 +15,11 @@ #include <sound/pcm_params.h> #include <sound/simple_card_utils.h> -static void simple_fixup_sample_fmt(struct simple_util_data *data, - struct snd_pcm_hw_params *params) +int simple_util_get_sample_fmt(struct simple_util_data *data) { int i; - struct snd_mask *mask = hw_param_mask(params, - SNDRV_PCM_HW_PARAM_FORMAT); + int val = -EINVAL; + struct { char *fmt; u32 val; @@ -33,11 +34,26 @@ static void simple_fixup_sample_fmt(struct simple_util_data *data, for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) { if (!strcmp(data->convert_sample_format, of_sample_fmt_table[i].fmt)) { - snd_mask_none(mask); - snd_mask_set(mask, of_sample_fmt_table[i].val); + val = of_sample_fmt_table[i].val; break; } } + return val; +} +EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt); + +static void simple_fixup_sample_fmt(struct simple_util_data *data, + struct snd_pcm_hw_params *params) +{ + int val; + struct snd_mask *mask = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); + + val = simple_util_get_sample_fmt(data); + if (val >= 0) { + snd_mask_none(mask); + snd_mask_set(mask, val); + } } void simple_util_parse_convert(struct device_node *np, @@ -46,6 +62,9 @@ void simple_util_parse_convert(struct device_node *np, { char prop[128]; + if (!np) + return; + if (!prefix) prefix = ""; @@ -117,8 +136,8 @@ EXPORT_SYMBOL_GPL(simple_util_parse_daifmt); int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, struct simple_util_dai *dai) { - u32 *array_values, *p; int n, i, ret; + u32 *p; if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) return 0; @@ -133,14 +152,15 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, if (!dai->tdm_width_map) return -ENOMEM; - array_values = kcalloc(n, sizeof(*array_values), GFP_KERNEL); + u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), + GFP_KERNEL); if (!array_values) return -ENOMEM; ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n); if (ret < 0) { dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret); - goto out; + return ret; } p = array_values; @@ -151,11 +171,8 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, } dai->n_tdm_widths = i; - ret = 0; -out: - kfree(array_values); - return ret; + return 0; } EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map); @@ -1126,24 +1143,88 @@ parse_dai_end: } EXPORT_SYMBOL_GPL(graph_util_parse_dai); -int graph_util_parse_link_direction(struct device_node *np, +void graph_util_parse_link_direction(struct device_node *np, bool *playback_only, bool *capture_only) { - bool is_playback_only = false; - bool is_capture_only = false; + bool is_playback_only = of_property_read_bool(np, "playback-only"); + bool is_capture_only = of_property_read_bool(np, "capture-only"); - is_playback_only = of_property_read_bool(np, "playback-only"); - is_capture_only = of_property_read_bool(np, "capture-only"); + if (is_playback_only) + *playback_only = is_playback_only; + if (is_capture_only) + *capture_only = is_capture_only; +} +EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); - if (is_playback_only && is_capture_only) - return -EINVAL; +static enum snd_soc_trigger_order +__graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + const char *prop) +{ + u32 val[SND_SOC_TRIGGER_SIZE]; + int ret; - *playback_only = is_playback_only; - *capture_only = is_capture_only; + ret = of_property_read_u32_array(np, prop, val, SND_SOC_TRIGGER_SIZE); + if (ret == 0) { + struct device *dev = simple_priv_to_dev(priv); + u32 order = (val[0] << 8) + + (val[1] << 4) + + (val[2]); + + switch (order) { + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_COMPONENT << 4) + + (SND_SOC_TRIGGER_DAI): + return SND_SOC_TRIGGER_ORDER_DEFAULT; + + case (SND_SOC_TRIGGER_LINK << 8) + + (SND_SOC_TRIGGER_DAI << 4) + + (SND_SOC_TRIGGER_COMPONENT): + return SND_SOC_TRIGGER_ORDER_LDC; + + default: + dev_err(dev, "unsupported trigger order [0x%x]\n", order); + } + } - return 0; + /* SND_SOC_TRIGGER_ORDER_MAX means error */ + return SND_SOC_TRIGGER_ORDER_MAX; } -EXPORT_SYMBOL_GPL(graph_util_parse_link_direction); + +void graph_util_parse_trigger_order(struct simple_util_priv *priv, + struct device_node *np, + enum snd_soc_trigger_order *trigger_start, + enum snd_soc_trigger_order *trigger_stop) +{ + static enum snd_soc_trigger_order order; + + /* + * We can use it like below + * + * #include <dt-bindings/sound/audio-graph.h> + * + * link-trigger-order = <SND_SOC_TRIGGER_LINK + * SND_SOC_TRIGGER_COMPONENT + * SND_SOC_TRIGGER_DAI>; + */ + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) { + *trigger_start = order; + *trigger_stop = order; + } + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-start"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_start = order; + + order = __graph_util_parse_trigger_order(priv, np, "link-trigger-order-stop"); + if (order < SND_SOC_TRIGGER_ORDER_MAX) + *trigger_stop = order; + + return; +} +EXPORT_SYMBOL_GPL(graph_util_parse_trigger_order); /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 9c79ff6a568f..d2588f1ea54e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -5,6 +5,7 @@ // Copyright (C) 2012 Renesas Solutions Corp. // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/module.h> @@ -129,24 +130,6 @@ static void simple_parse_convert(struct device *dev, of_node_put(node); } -static void simple_parse_mclk_fs(struct device_node *top, - struct device_node *np, - struct simple_dai_props *props, - char *prefix) -{ - struct device_node *node = of_get_parent(np); - char prop[128]; - - snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX); - of_property_read_u32(top, prop, &props->mclk_fs); - - snprintf(prop, sizeof(prop), "%smclk-fs", prefix); - of_property_read_u32(node, prop, &props->mclk_fs); - of_property_read_u32(np, prop, &props->mclk_fs); - - of_node_put(node); -} - static int simple_parse_node(struct simple_util_priv *priv, struct device_node *np, struct link_info *li, @@ -154,7 +137,6 @@ static int simple_parse_node(struct simple_util_priv *priv, int *cpu) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct snd_soc_dai_link_component *dlc; @@ -169,8 +151,6 @@ static int simple_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - simple_parse_mclk_fs(top, np, dai_props, prefix); - ret = simple_parse_dai(dev, np, dlc, cpu); if (ret) return ret; @@ -187,24 +167,59 @@ static int simple_parse_node(struct simple_util_priv *priv, } static int simple_link_init(struct simple_util_priv *priv, - struct device_node *node, + struct device_node *cpu, struct device_node *codec, struct link_info *li, char *prefix, char *name) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct device_node *node = of_get_parent(cpu); + enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; + enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; + bool playback_only = 0, capture_only = 0; int ret; ret = simple_util_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) - return 0; + goto init_end; + + graph_util_parse_link_direction(top, &playback_only, &capture_only); + graph_util_parse_link_direction(node, &playback_only, &capture_only); + graph_util_parse_link_direction(cpu, &playback_only, &capture_only); + graph_util_parse_link_direction(codec, &playback_only, &capture_only); + + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec, PREFIX "mclk-fs", &dai_props->mclk_fs); + + graph_util_parse_trigger_order(priv, top, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, node, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, cpu, &trigger_start, &trigger_stop); + graph_util_parse_trigger_order(priv, codec, &trigger_start, &trigger_stop); + + dai_link->playback_only = playback_only; + dai_link->capture_only = capture_only; + + dai_link->trigger_start = trigger_start; + dai_link->trigger_stop = trigger_stop; dai_link->init = simple_util_dai_init; dai_link->ops = &simple_ops; - return simple_util_set_dailink_name(dev, dai_link, name); + ret = simple_util_set_dailink_name(dev, dai_link, name); +init_end: + of_node_put(node); + + return ret; } static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, @@ -278,7 +293,7 @@ static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, snd_soc_dai_link_set_capabilities(dai_link); - ret = simple_link_init(priv, node, codec, li, prefix, dai_name); + ret = simple_link_init(priv, np, codec, li, prefix, dai_name); out_put_node: li->link++; @@ -336,7 +351,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv, simple_util_canonicalize_cpu(cpus, single_cpu); simple_util_canonicalize_platform(platforms, cpus); - ret = simple_link_init(priv, node, codec, li, prefix, dai_name); + ret = simple_link_init(priv, cpu, codec, li, prefix, dai_name); dai_link_of_err: of_node_put(plat); @@ -713,7 +728,6 @@ static int simple_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - struct link_info *li; int ret; /* Allocate the private data and the DAI link array */ @@ -727,7 +741,7 @@ static int simple_probe(struct platform_device *pdev) card->probe = simple_soc_probe; card->driver_name = "simple-card"; - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -804,7 +818,6 @@ static int simple_probe(struct platform_device *pdev) if (ret < 0) goto err; - devm_kfree(dev, li); return 0; err: simple_util_clean_reference(card); diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index e4967540a2e1..e9e5e235a8a6 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -189,7 +189,7 @@ static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } -static u64 test_dai_formats = +static const u64 test_dai_formats = /* * Select below from Sound Card, not auto * SND_SOC_POSSIBLE_DAIFMT_BP_FP diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 3bf37a8fd6e6..c8522e2430f8 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -18,7 +18,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-acpi.h> -#include <asm/intel-family.h> +#include <asm/cpu_device_id.h> #include "../utils.h" #define ES8336_CODEC_DAI "ES8316 HiFi" @@ -153,9 +153,9 @@ static int avs_es8336_hw_params(struct snd_pcm_substream *substream, int clk_freq; int ret; - switch (boot_cpu_data.x86_model) { - case INTEL_FAM6_KABYLAKE_L: - case INTEL_FAM6_KABYLAKE: + switch (boot_cpu_data.x86_vfm) { + case INTEL_KABYLAKE_L: + case INTEL_KABYLAKE: clk_freq = 24000000; break; default: diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 88e711875004..c76b86254a8b 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -341,7 +341,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + const struct snd_soc_pcm_stream *stream_info; struct hdac_ext_stream *link_stream; struct hdac_ext_link *link; struct avs_dma_data *data; @@ -637,7 +637,7 @@ static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_so static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_stream *stream_info; + const struct snd_soc_pcm_stream *stream_info; struct avs_dma_data *data; struct hdac_ext_stream *host_stream; unsigned int format_val; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index b6c5d94a1554..5cda527020c7 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1900,7 +1900,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ return 0; } -static struct snd_soc_tplg_ops avs_tplg_ops = { +static const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count = ARRAY_SIZE(avs_control_ops), .control_load = avs_control_load, diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 4e0586034de4..f1faa5dd2a4f 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -677,6 +677,8 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_CS42L43_SDW select MFD_CS42L43 select MFD_CS42L43_SDW + select PINCTRL_CS42L43 + select SPI_CS42L43 select SND_SOC_CS35L56_SPI select SND_SOC_CS35L56_SDW select SND_SOC_DMIC diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index 3ae26f21458f..3c7cee03a02e 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -131,7 +131,7 @@ static int bdw_rt5650_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops bdw_rt5650_ops = { +static const struct snd_soc_ops bdw_rt5650_ops = { .hw_params = bdw_rt5650_hw_params, }; diff --git a/sound/soc/intel/boards/ehl_rt5660.c b/sound/soc/intel/boards/ehl_rt5660.c index 686e60321224..26289e8fdd87 100644 --- a/sound/soc/intel/boards/ehl_rt5660.c +++ b/sound/soc/intel/boards/ehl_rt5660.c @@ -132,7 +132,7 @@ static int rt5660_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops rt5660_ops = { +static const struct snd_soc_ops rt5660_ops = { .hw_params = rt5660_hw_params, }; diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 9dbc15f9d1c9..154f6a74ed15 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -354,7 +354,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -388,7 +388,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index e662da5af83b..02ed77a07e23 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -288,7 +288,7 @@ static int kabylake_ssp0_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, .trigger = kabylake_ssp0_trigger, }; @@ -535,7 +535,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -569,7 +569,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index 894d127c482a..66885cb36f24 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -277,7 +277,7 @@ static int kabylake_rt5660_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5660_ops = { +static const struct snd_soc_ops kabylake_rt5660_ops = { .hw_params = kabylake_rt5660_hw_params, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index e16c42e81eca..9da89436a917 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -489,7 +489,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5663_ops = { +static const struct snd_soc_ops kabylake_rt5663_ops = { .hw_params = kabylake_rt5663_hw_params, }; @@ -539,7 +539,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, }; @@ -575,7 +575,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; @@ -609,7 +609,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylake_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index a9501cd106ff..a32ce8f972f3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -424,7 +424,7 @@ static int kabylake_rt5663_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_rt5663_ops = { +static const struct snd_soc_ops kabylake_rt5663_ops = { .hw_params = kabylake_rt5663_hw_params, }; @@ -469,7 +469,7 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops kabylake_ssp0_ops = { +static const struct snd_soc_ops kabylake_ssp0_ops = { .hw_params = kabylake_ssp0_hw_params, }; @@ -508,7 +508,7 @@ static int kabylake_dmic_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } -static struct snd_soc_ops kabylake_dmic_ops = { +static const struct snd_soc_ops kabylake_dmic_ops = { .startup = kabylake_dmic_startup, }; diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index dfcc2c5c25cc..faba847bb7c9 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -86,12 +86,10 @@ enum { /* * sof_da7219_private: private data for da7219 machine driver * - * @is_jsl_board: true for JSL boards * @mclk_en: true for mclk pin is connected * @pll_bypass: true for PLL bypass mode */ struct sof_da7219_private { - bool is_jsl_board; bool mclk_en; bool pll_bypass; }; diff --git a/sound/soc/intel/boards/sof_da7219.c b/sound/soc/intel/boards/sof_da7219.c index 886771e9b9d6..fa1f7d2d8278 100644 --- a/sound/soc/intel/boards/sof_da7219.c +++ b/sound/soc/intel/boards/sof_da7219.c @@ -178,42 +178,21 @@ static void da7219_codec_exit(struct snd_soc_pcm_runtime *rtd) snd_soc_component_set_jack(component, NULL, NULL); } -static int max98373_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int card_late_probe(struct snd_soc_card *card) { - struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream); - int ret, j; - - for (j = 0; j < runtime->dai_link->num_codecs; j++) { - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, j); - - if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { - /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 3, 4, 16); - if (ret < 0) { - dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); - return ret; - } - } - if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { - /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC, 3, 4, 16); - if (ret < 0) { - dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); - return ret; - } - } + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = &card->dapm; + int err; + + if (ctx->amp_type == CODEC_MAX98373) { + /* Disable Left and Right Spk pin after boot */ + snd_soc_dapm_disable_pin(dapm, "Left Spk"); + snd_soc_dapm_disable_pin(dapm, "Right Spk"); + err = snd_soc_dapm_sync(dapm); + if (err < 0) + return err; } - return 0; -} - -static const struct snd_soc_ops max98373_ops = { - .hw_params = max98373_hw_params, -}; - -static int card_late_probe(struct snd_soc_card *card) -{ return sof_intel_board_card_late_probe(card); } @@ -276,14 +255,6 @@ sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, break; case CODEC_MAX98373: max_98373_dai_link(dev, ctx->amp_link); - - if (ctx->da7219.is_jsl_board) { - ctx->amp_link->ops = &max98373_ops; /* use local ops */ - } else { - /* TBD: implement the amp for later platform */ - dev_err(dev, "max98373 not support yet\n"); - return -EINVAL; - } break; case CODEC_MAX98390: max_98390_dai_link(dev, ctx->amp_link); @@ -388,8 +359,6 @@ static int audio_probe(struct platform_device *pdev) break; } } else if (board_quirk & SOF_DA7219_JSL_BOARD) { - ctx->da7219.is_jsl_board = true; - /* overwrite the DAI link order for JSL boards */ ctx->link_order_overwrite = JSL_LINK_ORDER; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index c1fcc156a575..2a88efaa6d26 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -371,7 +371,7 @@ static int sof_es8336_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops sof_es8336_ops = { +static const struct snd_soc_ops sof_es8336_ops = { .hw_params = sof_es8336_hw_params, .trigger = sof_8336_trigger, }; diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 6c40ecc04723..fcc3b95e57a4 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -9,6 +9,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-dai.h> #include <sound/soc-dapm.h> +#include <sound/sof.h> #include <uapi/sound/asound.h> #include "../common/soc-intel-quirks.h" #include "sof_maxim_common.h" @@ -72,26 +73,115 @@ static struct snd_soc_dai_link_component max_98373_components[] = { }, }; +/* + * According to the definition of 'DAI Sel Mux' mixer in max98373.c, rx mask + * should choose two channels from TDM slots, the LSB of rx mask is left channel + * and the other one is right channel. + */ +static const struct { + unsigned int rx; +} max_98373_tdm_mask[] = { + {.rx = 0x3}, + {.rx = 0x3}, +}; + +/* + * The tx mask indicates which channel(s) contains output IV-sense data and + * others should set to Hi-Z. Here we get the channel number from codec's ACPI + * device property "maxim,vmon-slot-no" and "maxim,imon-slot-no" to generate the + * mask. Refer to the max98373_slot_config() function in max98373.c codec driver. + */ +static unsigned int max_98373_get_tx_mask(struct device *dev) +{ + int vmon_slot; + int imon_slot; + + if (device_property_read_u32(dev, "maxim,vmon-slot-no", &vmon_slot)) + vmon_slot = 0; + + if (device_property_read_u32(dev, "maxim,imon-slot-no", &imon_slot)) + imon_slot = 1; + + dev_dbg(dev, "vmon_slot %d imon_slot %d\n", vmon_slot, imon_slot); + + return (0x1 << vmon_slot) | (0x1 << imon_slot); +} + static int max_98373_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *codec_dai; + int i; + int tdm_slots; + unsigned int tx_mask; + unsigned int tx_mask_used = 0x0; int ret = 0; - int j; - for_each_rtd_codec_dais(rtd, j, codec_dai) { - if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { - /* DEV0 tdm slot configuration */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); - } else if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { - /* DEV1 tdm slot configuration */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (i >= ARRAY_SIZE(max_98373_tdm_mask)) { + dev_err(codec_dai->dev, "only 2 amps are supported\n"); + return -EINVAL; } - if (ret < 0) { - dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", - ret); - return ret; + + switch (dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* get the tplg configured tdm slot number */ + tdm_slots = sof_dai_get_tdm_slots(rtd); + if (tdm_slots <= 0) { + dev_err(rtd->dev, "invalid tdm slots %d\n", + tdm_slots); + return -EINVAL; + } + + /* get the tx mask from ACPI device properties */ + tx_mask = max_98373_get_tx_mask(codec_dai->dev); + if (!tx_mask) + return -EINVAL; + + if (tx_mask & tx_mask_used) { + dev_err(codec_dai->dev, "invalid tx mask 0x%x, used 0x%x\n", + tx_mask, tx_mask_used); + return -EINVAL; + } + + tx_mask_used |= tx_mask; + + /* + * check if tdm slot number is too small for channel + * allocation + */ + if (fls(tx_mask) > tdm_slots) { + dev_err(codec_dai->dev, "slot mismatch, tx %d slots %d\n", + fls(tx_mask), tdm_slots); + return -EINVAL; + } + + if (fls(max_98373_tdm_mask[i].rx) > tdm_slots) { + dev_err(codec_dai->dev, "slot mismatch, rx %d slots %d\n", + fls(max_98373_tdm_mask[i].rx), tdm_slots); + return -EINVAL; + } + + dev_dbg(codec_dai->dev, "set tdm slot: tx 0x%x rx 0x%x slots %d width %d\n", + tx_mask, max_98373_tdm_mask[i].rx, + tdm_slots, params_width(params)); + + ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_mask, + max_98373_tdm_mask[i].rx, + tdm_slots, + params_width(params)); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", + ret); + return ret; + } + break; + default: + dev_dbg(codec_dai->dev, "codec is in I2S mode\n"); + break; } } return 0; diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index c08b4eef0bcb..bfe17acbc161 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -115,7 +115,7 @@ static int sof_nau8825_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops sof_nau8825_ops = { +static const struct snd_soc_ops sof_nau8825_ops = { .hw_params = sof_nau8825_hw_params, }; diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index dda346e0f737..f52e25083905 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -452,7 +452,7 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops rt1015_ops = { +static const struct snd_soc_ops rt1015_ops = { .hw_params = rt1015_hw_params, }; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 6fc6eb0c5172..23a40b913290 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -397,7 +397,7 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops sof_rt5682_ops = { +static const struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index e41b0d95e0ff..e5feaef669d1 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -280,6 +280,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { { .callback = sof_sdw_quirk_cb, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "0000000000070000"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2_100K), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), DMI_MATCH(DMI_PRODUCT_NAME, "Brya"), }, @@ -400,6 +409,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { { .callback = sof_sdw_quirk_cb, .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B8C"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HP"), DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16"), }, @@ -505,6 +523,22 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(RT711_JD2), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE3") + }, + .driver_data = (void *)(SOF_SIDECAR_AMPS), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE4") + }, + .driver_data = (void *)(SOF_SIDECAR_AMPS), + }, {} }; @@ -559,24 +593,6 @@ static const struct snd_kcontrol_new rt700_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -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; -} - /* these wrappers are only needed to avoid typecast compilation errors */ int sdw_startup(struct snd_pcm_substream *substream) { @@ -1077,6 +1093,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_cs_amp_init, .rtd_init = cs_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), }, @@ -1112,6 +1130,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .rtd_init = cs42l43_hs_rtd_init, + .controls = generic_jack_controls, + .num_controls = ARRAY_SIZE(generic_jack_controls), .widgets = generic_jack_widgets, .num_widgets = ARRAY_SIZE(generic_jack_widgets), }, @@ -1137,6 +1157,8 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_cs42l43_spk_init, .rtd_init = cs42l43_spk_rtd_init, + .controls = generic_spk_controls, + .num_controls = ARRAY_SIZE(generic_spk_controls), .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), .quirk = SOF_CODEC_SPKR | SOF_SIDECAR_AMPS, @@ -2114,9 +2136,9 @@ static int mc_probe(struct platform_device *pdev) card = &ctx->card; card->dev = &pdev->dev; - card->name = "soundwire", - card->owner = THIS_MODULE, - card->late_probe = sof_sdw_card_late_probe, + card->name = "soundwire"; + card->owner = THIS_MODULE; + card->late_probe = sof_sdw_card_late_probe; snd_soc_card_set_drvdata(card, ctx); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 3dfba6f6b95d..2a3145d1feb6 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -134,10 +134,6 @@ struct mc_private { extern unsigned long sof_sdw_quirk; -struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, - const char * const dai_name[], - int num_dais); - int sdw_startup(struct snd_pcm_substream *substream); int sdw_prepare(struct snd_pcm_substream *substream); int sdw_trigger(struct snd_pcm_substream *substream, int cmd); @@ -169,7 +165,7 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* RT1308 I2S support */ -extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; +extern const struct snd_soc_ops sof_sdw_rt1308_i2s_ops; /* generic amp support */ int sof_sdw_rt_amp_init(struct snd_soc_card *card, diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index fdb75fc71c26..fc18e4aa3dbe 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -36,24 +36,15 @@ static struct snd_soc_jack_pin cs42l42_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "cs42l42" -}; - int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - 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; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l42", card->components); diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 96f193798540..67737815d016 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -35,24 +35,15 @@ static struct snd_soc_jack_pin rt5682_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt5682" -}; - int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - 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; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt5682", card->components); diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index f9575db9d99c..0db730071be2 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -33,24 +33,15 @@ static struct snd_soc_jack_pin rt700_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt700" -}; - int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - 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; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt700", card->components); diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index d49e5aa786c3..60ff4d88e2dc 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -59,24 +59,15 @@ static struct snd_soc_jack_pin rt711_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt711" -}; - int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - 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; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt711", card->components); diff --git a/sound/soc/intel/boards/sof_sdw_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index 797ea9ffa77a..d1c0f91ce589 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_amp.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -233,7 +233,7 @@ static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { +const struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { .hw_params = rt1308_i2s_hw_params, }; diff --git a/sound/soc/intel/boards/sof_sdw_rt_dmic.c b/sound/soc/intel/boards/sof_sdw_rt_dmic.c index b8b493d5c6ec..ea7c1a4bc566 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_dmic.c +++ b/sound/soc/intel/boards/sof_sdw_rt_dmic.c @@ -12,25 +12,13 @@ #include "sof_board_helpers.h" #include "sof_sdw_common.h" -static const char * const dmics[] = { - "rt715", - "rt715-sdca", - "rt712-sdca-dmic", - "rt722-sdca", -}; - int rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_soc_card *card = rtd->card; struct snd_soc_component *component; - struct snd_soc_dai *codec_dai; char *mic_name; - codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; /* * rt715-sdca (aka rt714) is a special case that uses different name in card->components 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 012195c50519..4254e30ee4c3 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 @@ -74,10 +74,6 @@ static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { }, }; -static const char * const jack_codecs[] = { - "rt711", "rt712", "rt713", "rt722" -}; - /* * The sdca suffix is required for rt711 since there are two generations of the same chip. * RT713 is an SDCA device but the sdca suffix is required for backwards-compatibility with @@ -91,17 +87,12 @@ int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *d { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; int i; - codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); - if (!codec_dai) - return -EINVAL; - - component = codec_dai->component; + component = dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:%s", card->components, component->name_prefix); diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c index 4cb0d463bf40..b2d02cc92a6a 100644 --- a/sound/soc/intel/boards/sof_wm8804.c +++ b/sound/soc/intel/boards/sof_wm8804.c @@ -148,7 +148,7 @@ static int sof_wm8804_hw_params(struct snd_pcm_substream *substream, } /* machine stream operations */ -static struct snd_soc_ops sof_wm8804_ops = { +static const struct snd_soc_ops sof_wm8804_ops = { .hw_params = sof_wm8804_hw_params, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 79d26e0f2c28..cc87c34e5a08 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -15,6 +15,42 @@ static const struct snd_soc_acpi_endpoint single_endpoint = { .group_id = 0, }; +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { + { + .adr = 0x00003001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { .adr = 0x000020025D071100ull, @@ -33,6 +69,14 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_link_adr arl_cs42l43_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, +}; + static const struct snd_soc_acpi_link_adr arl_rvp[] = { { .mask = BIT(0), @@ -59,6 +103,12 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { { + .link_mask = BIT(0), + .links = arl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", + }, + { .link_mask = 0x1, /* link0 required */ .links = arl_rvp, .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 b0a49e28ab09..bc8817633b81 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -30,6 +30,42 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { + { + .adr = 0x00003001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { .adr = 0x000020025D071100ull, @@ -156,6 +192,14 @@ static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { } }; +static const struct snd_soc_acpi_link_adr rpl_cs42l43_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, +}; + static const struct snd_soc_acpi_link_adr rpl_sdca_3_in_1[] = { { .mask = BIT(0), @@ -447,6 +491,12 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = { { + .link_mask = BIT(0), + .links = rpl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-rpl-cs42l43-l0.tplg", + }, + { .link_mask = 0xF, /* 4 active links required */ .links = rpl_sdca_3_in_1, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e27f0fc3d897..602ef4321122 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3470,7 +3470,7 @@ static int skl_tplg_complete(struct snd_soc_component *component) return 0; } -static struct snd_soc_tplg_ops skl_tplg_ops = { +static const struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, .bytes_ext_ops = skl_tlv_ops, diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index 59abe0b3c59f..7e6090af720b 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -32,7 +32,7 @@ static const struct snd_pcm_hardware axg_fifo_hw = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), .formats = AXG_FIFO_FORMATS, .rate_min = 5512, - .rate_max = 384000, + .rate_max = 768000, .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, .period_bytes_min = AXG_FIFO_BURST, diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index e97d43ae7fd2..e70c8c34c7db 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -112,7 +112,7 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, @@ -189,7 +189,7 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h index daaca10fec9e..1a17f546ce6e 100644 --- a/sound/soc/meson/axg-tdm.h +++ b/sound/soc/meson/axg-tdm.h @@ -16,7 +16,7 @@ #define AXG_TDM_NUM_LANES 4 #define AXG_TDM_CHANNEL_MAX 128 #define AXG_TDM_RATES (SNDRV_PCM_RATE_5512 | \ - SNDRV_PCM_RATE_8000_384000) + SNDRV_PCM_RATE_8000_768000) #define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index e03a6e21c1c6..03512da4092b 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -131,7 +131,7 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, @@ -228,7 +228,7 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .channels_max = AXG_FIFO_CH_MAX, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 5515, - .rate_max = 384000, + .rate_max = 768000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 3d02aa3844f2..56b4a3654aec 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -8,9 +8,19 @@ #include <linux/input-event-codes.h> #include "common.h" +#define NAME_SIZE 32 + static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; int qcom_snd_parse_of(struct snd_soc_card *card) @@ -240,5 +250,30 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup); +int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *dp_jack, int dp_pcm_id) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_card *card = rtd->card; + char jack_name[NAME_SIZE]; + int rval, i; + + snprintf(jack_name, sizeof(jack_name), "DP%d Jack", dp_pcm_id); + rval = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, dp_jack); + if (rval) + return rval; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + rval = snd_soc_component_set_jack(codec_dai->component, dp_jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_snd_dp_jack_setup); + MODULE_DESCRIPTION("ASoC Qualcomm helper functions"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index d7f80ee5ae26..1b8d3f90bffa 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -9,5 +9,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); +int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *dp_jack, int id); + #endif diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index b0f3e02cb043..5a47f661e0c6 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -1166,9 +1166,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm"); + if (!res) + return -EINVAL; drvdata->rxtx_cdc_dma_lpm_buf = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm"); + if (!res) + return -EINVAL; drvdata->va_cdc_dma_lpm_buf = res->start; } diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 5291deac0a0b..4ebaaf736fb9 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -267,7 +267,7 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt); -static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels) +void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels) { if (num_channels == 1) { ch_map[0] = PCM_CHANNEL_FL; @@ -281,6 +281,7 @@ static void audioreach_set_channel_mapping(u8 *ch_map, int num_channels) ch_map[3] = PCM_CHANNEL_RS; } } +EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping); static void apm_populate_container_config(struct apm_container_obj *cfg, struct audioreach_container *cont) @@ -819,7 +820,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, uint32_t num_channels = cfg->num_channels; int payload_size; struct gpr_pkt *pkt; - int rc; + int rc, i; void *p; payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + @@ -842,18 +843,8 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, media_format->sample_rate = cfg->sample_rate; media_format->bit_width = cfg->bit_width; media_format->num_channels = cfg->num_channels; - - if (num_channels == 1) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - } else if (num_channels == 2) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - media_format->channel_mapping[1] = PCM_CHANNEL_FR; - } else if (num_channels == 4) { - media_format->channel_mapping[0] = PCM_CHANNEL_FL; - media_format->channel_mapping[1] = PCM_CHANNEL_FR; - media_format->channel_mapping[2] = PCM_CHANNEL_LS; - media_format->channel_mapping[3] = PCM_CHANNEL_RS; - } + for (i = 0; i < num_channels; i++) + media_format->channel_mapping[i] = cfg->channel_map[i]; rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -883,9 +874,6 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, mp3_cfg->q_factor = mcfg->bit_width - 1; mp3_cfg->endianness = PCM_LITTLE_ENDIAN; mp3_cfg->num_channels = mcfg->num_channels; - - audioreach_set_channel_mapping(mp3_cfg->channel_mapping, - mcfg->num_channels); break; case SND_AUDIOCODEC_AAC: media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; @@ -1104,9 +1092,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->num_channels = mcfg->num_channels; media_cfg->q_factor = mcfg->bit_width - 1; media_cfg->bits_per_sample = mcfg->bit_width; - - audioreach_set_channel_mapping(media_cfg->channel_mapping, - num_channels); + memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); @@ -1163,9 +1149,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, cfg->q_factor = mcfg->bit_width - 1; cfg->endianness = PCM_LITTLE_ENDIAN; cfg->num_channels = mcfg->num_channels; - - audioreach_set_channel_mapping(cfg->channel_mapping, - num_channels); + memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); } else { rc = audioreach_set_compr_media_format(header, p, mcfg); if (rc) { diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 2c82917b7162..61a69df4f50f 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -755,7 +755,6 @@ struct audioreach_module_config { u16 data_format; u16 num_channels; - u16 active_channels_mask; u16 dp_idx; u32 channel_allocation; u32 sd_line_mask; @@ -767,6 +766,7 @@ struct audioreach_module_config { /* Packet Allocation routines */ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token); +void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels); void *audioreach_alloc_cmd_pkt(int payload_size, uint32_t opcode, uint32_t token, uint32_t src_port, uint32_t dest_port); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index a9c4f896a7df..7d9628cda875 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -172,8 +172,8 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, } static int q6tdm_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -250,8 +250,10 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream, } static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_ch_mask, - unsigned int rx_num, unsigned int *rx_ch_mask) + unsigned int tx_num, + const unsigned int *tx_ch_mask, + unsigned int rx_num, + const unsigned int *rx_ch_mask) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -407,8 +409,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, } static int q6slim_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, + const unsigned int *tx_slot, + unsigned int rx_num, + const unsigned int *rx_slot) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id]; diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index c7df8343b2dc..c9404b5934c7 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -239,6 +239,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.num_channels = runtime->channels; cfg.bit_width = prtd->bits_per_sample; cfg.fmt = SND_AUDIOCODEC_PCM; + audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels); if (prtd->state) { /* clear the previous setup if any */ @@ -665,6 +666,8 @@ static int q6apm_dai_compr_set_params(struct snd_soc_component *component, cfg.num_channels = 2; cfg.bit_width = prtd->bits_per_sample; cfg.fmt = codec->id; + audioreach_set_default_channel_mapping(cfg.channel_map, + cfg.num_channels); memcpy(&cfg.codec, codec, sizeof(*codec)); ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 66b911b49e3f..9c98a35ad099 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -25,13 +25,15 @@ struct q6apm_lpass_dai_data { }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_ch_mask, - unsigned int rx_num, unsigned int *rx_ch_mask) + unsigned int tx_num, + const unsigned int *tx_ch_mask, + unsigned int rx_num, + const unsigned int *rx_ch_mask) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; - int ch_mask; + int i; switch (dai->id) { case WSA_CODEC_DMA_TX_0: @@ -56,7 +58,8 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, tx_num); return -EINVAL; } - ch_mask = *tx_ch_mask; + for (i = 0; i < tx_num; i++) + cfg->channel_map[i] = tx_ch_mask[i]; break; case WSA_CODEC_DMA_RX_0: @@ -79,7 +82,8 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, rx_num); return -EINVAL; } - ch_mask = *rx_ch_mask; + for (i = 0; i < rx_num; i++) + cfg->channel_map[i] = rx_ch_mask[i]; break; default: @@ -88,8 +92,6 @@ static int q6dma_set_channel_map(struct snd_soc_dai *dai, return -EINVAL; } - cfg->active_channels_mask = ch_mask; - return 0; } @@ -104,6 +106,7 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream, cfg->bit_width = params_width(params); cfg->sample_rate = params_rate(params); cfg->num_channels = channels; + audioreach_set_default_channel_mapping(cfg->channel_map, channels); switch (dai->id) { case DISPLAY_PORT_RX_0: @@ -128,10 +131,12 @@ static int q6dma_hw_params(struct snd_pcm_substream *substream, { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + int channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max; cfg->bit_width = params_width(params); cfg->sample_rate = params_rate(params); - cfg->num_channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max; + cfg->num_channels = channels; + audioreach_set_default_channel_mapping(cfg->channel_map, channels); return 0; } diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 70572c83e101..83319a928f29 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020, Linaro Limited +#include <linux/cleanup.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> @@ -730,6 +731,29 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, return 0; } +static int audioreach_widget_dp_module_load(struct audioreach_module *mod, + struct snd_soc_tplg_vendor_array *mod_array) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + static int audioreach_widget_load_buffer(struct snd_soc_component *component, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) @@ -760,6 +784,9 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, case MODULE_ID_I2S_SOURCE: audioreach_widget_i2s_module_load(mod, mod_array); break; + case MODULE_ID_DISPLAY_PORT_SINK: + audioreach_widget_dp_module_load(mod, mod_array); + break; default: return -EINVAL; } @@ -1240,7 +1267,7 @@ static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = { audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw}, }; -static struct snd_soc_tplg_ops audioreach_tplg_ops = { +static const struct snd_soc_tplg_ops audioreach_tplg_ops = { .io_ops = audioreach_io_ops, .io_ops_count = ARRAY_SIZE(audioreach_io_ops), @@ -1262,18 +1289,19 @@ int audioreach_tplg_init(struct snd_soc_component *component) struct snd_soc_card *card = component->card; struct device *dev = component->dev; const struct firmware *fw; - char *tplg_fw_name; int ret; /* Inline with Qualcomm UCM configs and linux-firmware path */ - tplg_fw_name = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", card->driver_name, card->name); + char *tplg_fw_name __free(kfree) = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", + card->driver_name, + card->name); if (!tplg_fw_name) return -ENOMEM; ret = request_firmware(&fw, tplg_fw_name, dev); if (ret < 0) { dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); - goto err; + return ret; } ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw); @@ -1283,8 +1311,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) } release_firmware(fw); -err: - kfree(tplg_fw_name); return ret; } diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 06fd47c4178f..922ecada1cd8 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -19,6 +19,7 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; bool jack_setup; }; @@ -27,6 +28,8 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_card *card = rtd->card; + struct snd_soc_jack *dp_jack = NULL; + int dp_pcm_id = 0; switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: @@ -41,10 +44,22 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); snd_soc_limit_volume(card, "SpkrRight PA Volume", 17); break; + case DISPLAY_PORT_RX_0: + /* DISPLAY_PORT dai ids are not contiguous */ + dp_pcm_id = 0; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; default: break; } + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index 0e0773a85809..898b5c26bf1e 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -12,6 +12,7 @@ #include "common.h" #include "qdsp6/q6afe.h" +#include "qdsp6/q6dsp-common.h" #include "sdw.h" struct x1e80100_snd_data { @@ -19,12 +20,32 @@ struct x1e80100_snd_data { struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; bool jack_setup; }; static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) { struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_jack *dp_jack = NULL; + int dp_pcm_id = 0; + + switch (cpu_dai->id) { + case DISPLAY_PORT_RX_0: + dp_pcm_id = 0; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: + dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1; + dp_jack = &data->dp_jack[dp_pcm_id]; + break; + default: + break; + } + + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } @@ -80,6 +101,23 @@ static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + const unsigned int rx_slot[4] = { PCM_CHANNEL_FL, + PCM_CHANNEL_LB, + PCM_CHANNEL_FR, + PCM_CHANNEL_RB }; + int ret; + + switch (cpu_dai->id) { + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + ARRAY_SIZE(rx_slot), rx_slot); + if (ret) + return ret; + break; + default: + break; + } return qcom_snd_sdw_prepare(substream, sruntime, &data->stream_prepared[cpu_dai->id]); diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b0c3ef030e06..b378f870b3ad 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -11,7 +11,6 @@ #include <linux/mfd/syscon.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/clk.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 1a24b78e9e02..eb9d5dee196e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/of_gpio.h> #include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/mfd/syscon.h> diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 93c2b1b08d0a..4b1ea7b2c796 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -140,7 +140,7 @@ config SND_SOC_SAMSUNG_ARIES_WM8994 config SND_SOC_SAMSUNG_MIDAS_WM1811 tristate "SoC I2S Audio support for Midas boards" - depends on SND_SOC_SAMSUNG + depends on SND_SOC_SAMSUNG && IIO select SND_SAMSUNG_I2S select SND_SOC_WM8994 help diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index a548ac33dd94..01716df0c842 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ #include <linux/extcon.h> +#include <linux/gpio/consumer.h> #include <linux/iio/consumer.h> #include <linux/input-event-codes.h> #include <linux/mfd/wm8994/registers.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <sound/jack.h> #include <sound/pcm_params.h> diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 0841e2e6f8ce..bbfe5fef59af 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -7,10 +7,11 @@ #include <linux/clk.h> #include <linux/gpio/consumer.h> +#include <linux/iio/consumer.h> #include <linux/mfd/wm8994/registers.h> +#include <linux/input-event-codes.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/regulator/consumer.h> #include <sound/jack.h> #include <sound/soc.h> #include <sound/soc-dapm.h> @@ -27,10 +28,11 @@ #define DEFAULT_FLL1_RATE 11289600U struct midas_priv { - struct regulator *reg_mic_bias; - struct regulator *reg_submic_bias; struct gpio_desc *gpio_fm_sel; struct gpio_desc *gpio_lineout_sel; + struct gpio_desc *gpio_headset_detect; + struct gpio_desc *gpio_headset_key; + struct iio_channel *adc_headset_detect; unsigned int fll1_rate; struct snd_soc_jack headset_jack; @@ -47,6 +49,117 @@ static struct snd_soc_jack_pin headset_jack_pins[] = { }, }; +/* + * min_mv/max_mv values in this struct are set up based on DT values. + */ +static struct snd_soc_jack_zone headset_jack_zones[] = { + { .jack_type = SND_JACK_HEADPHONE, }, + { .jack_type = SND_JACK_HEADSET, }, + { .jack_type = SND_JACK_HEADPHONE, }, +}; + +/* + * This is used for manual detection in headset_key_check, we reuse the + * structure since it's convenient. + * + * min_mv/max_mv values in this struct are set up based on DT values. + */ +static struct snd_soc_jack_zone headset_key_zones[] = { + { .jack_type = SND_JACK_BTN_0, }, /* Media */ + { .jack_type = SND_JACK_BTN_1, }, /* Volume Up */ + { .jack_type = SND_JACK_BTN_2, }, /* Volume Down */ +}; + +static int headset_jack_check(void *data) +{ + struct snd_soc_component *codec = data; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card); + int adc, ret; + int jack_type = 0; + + if (!gpiod_get_value_cansleep(priv->gpio_headset_detect)) + return 0; + + /* Enable headset mic bias regulator so that the ADC reading works */ + ret = snd_soc_dapm_force_enable_pin(dapm, "headset-mic-bias"); + if (ret < 0) { + pr_err("%s: Failed to enable headset mic bias regulator (%d), assuming headphones\n", + __func__, ret); + return SND_JACK_HEADPHONE; + } + snd_soc_dapm_sync(dapm); + + /* Sleep for a small amount of time to get the value to stabilize */ + msleep(20); + + ret = iio_read_channel_processed(priv->adc_headset_detect, &adc); + if (ret) { + pr_err("%s: Failed to read ADC (%d), assuming headphones\n", + __func__, ret); + jack_type = SND_JACK_HEADPHONE; + goto out; + } + pr_debug("%s: ADC value is %d\n", __func__, adc); + + jack_type = snd_soc_jack_get_type(&priv->headset_jack, adc); + +out: + ret = snd_soc_dapm_disable_pin(dapm, "headset-mic-bias"); + if (ret < 0) + pr_err("%s: Failed to disable headset mic bias regulator (%d)\n", + __func__, ret); + snd_soc_dapm_sync(dapm); + + return jack_type; +} + +static int headset_key_check(void *data) +{ + struct snd_soc_component *codec = data; + struct midas_priv *priv = snd_soc_card_get_drvdata(codec->card); + int adc, i, ret; + + if (!gpiod_get_value_cansleep(priv->gpio_headset_key)) + return 0; + + /* Filter out keypresses when 4 pole jack not detected */ + if (!(priv->headset_jack.status & SND_JACK_MICROPHONE)) + return 0; + + ret = iio_read_channel_processed(priv->adc_headset_detect, &adc); + if (ret) { + pr_err("%s: Failed to read ADC (%d), can't detect key type\n", + __func__, ret); + return 0; + } + pr_debug("%s: ADC value is %d\n", __func__, adc); + + for (i = 0; i < ARRAY_SIZE(headset_key_zones); i++) { + if (adc >= headset_key_zones[i].min_mv && + adc <= headset_key_zones[i].max_mv) { + return headset_key_zones[i].jack_type; + } + } + + return 0; +} + +static struct snd_soc_jack_gpio headset_gpio[] = { + { + .name = "Headset Jack", + .report = SND_JACK_HEADSET, + .debounce_time = 150, + .jack_status_check = headset_jack_check, + }, + { + .name = "Headset Key", + .report = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, + .debounce_time = 30, + .jack_status_check = headset_key_check, + }, +}; + static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) { struct snd_soc_card *card = rtd->card; @@ -169,38 +282,6 @@ static int midas_ext_spkmode(struct snd_soc_dapm_widget *w, return ret; } -static int midas_mic_bias(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_card *card = w->dapm->card; - struct midas_priv *priv = snd_soc_card_get_drvdata(card); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - return regulator_enable(priv->reg_mic_bias); - case SND_SOC_DAPM_POST_PMD: - return regulator_disable(priv->reg_mic_bias); - } - - return 0; -} - -static int midas_submic_bias(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_card *card = w->dapm->card; - struct midas_priv *priv = snd_soc_card_get_drvdata(card); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - return regulator_enable(priv->reg_submic_bias); - case SND_SOC_DAPM_POST_PMD: - return regulator_disable(priv->reg_submic_bias); - } - - return 0; -} - static int midas_fm_set(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -272,8 +353,19 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias), - SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias), + SND_SOC_DAPM_REGULATOR_SUPPLY("headset-mic-bias", 0, 0), + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("mic-bias", 0, 0), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_REGULATOR_SUPPLY("submic-bias", 0, 0), +}; + +/* Default routing; supplemented by audio-routing DT property */ +static const struct snd_soc_dapm_route midas_dapm_routes[] = { + /* Bind microphones with their respective regulator supplies */ + {"Main Mic", NULL, "mic-bias"}, + {"Sub Mic", NULL, "submic-bias"}, + {"Headset Mic", NULL, "headset-mic-bias"}, }; static int midas_set_bias_level(struct snd_soc_card *card, @@ -315,18 +407,67 @@ static int midas_late_probe(struct snd_soc_card *card) return ret; } - ret = snd_soc_card_jack_new_pins(card, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &priv->headset_jack, - headset_jack_pins, - ARRAY_SIZE(headset_jack_pins)); - if (ret) + if (!priv->gpio_headset_detect) { + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); + if (ret) + return ret; + + wm8958_mic_detect(aif1_dai->component, &priv->headset_jack, + NULL, NULL, NULL, NULL); + } else { + /* Some devices (n8000, t310) use a GPIO to detect the jack. */ + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); + if (ret) { + dev_err(card->dev, + "Failed to set up headset pins: %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_zones(&priv->headset_jack, + ARRAY_SIZE(headset_jack_zones), + headset_jack_zones); + if (ret) { + dev_err(card->dev, + "Failed to set up headset zones: %d\n", ret); + return ret; + } + + headset_gpio[0].data = aif1_dai->component; + headset_gpio[0].desc = priv->gpio_headset_detect; + + headset_gpio[1].data = aif1_dai->component; + headset_gpio[1].desc = priv->gpio_headset_key; + + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(priv->headset_jack.jack, + SND_JACK_BTN_2, KEY_VOLUMEDOWN); + + ret = snd_soc_jack_add_gpios(&priv->headset_jack, + ARRAY_SIZE(headset_gpio), + headset_gpio); + if (ret) + dev_err(card->dev, + "Failed to set up headset jack GPIOs: %d\n", + ret); + return ret; + } - wm8958_mic_detect(aif1_dai->component, &priv->headset_jack, - NULL, NULL, NULL, NULL); return 0; } @@ -421,6 +562,8 @@ static struct snd_soc_card midas_card = { .num_controls = ARRAY_SIZE(midas_controls), .dapm_widgets = midas_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(midas_dapm_widgets), + .dapm_routes = midas_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(midas_dapm_routes), .set_bias_level = midas_set_bias_level, .late_probe = midas_late_probe, @@ -433,6 +576,9 @@ static int midas_probe(struct platform_device *pdev) struct snd_soc_card *card = &midas_card; struct device *dev = &pdev->dev; static struct snd_soc_dai_link *dai_link; + enum iio_chan_type channel_type; + u32 fourpole_threshold[2]; + u32 button_threshold[3]; struct midas_priv *priv; int ret, i; @@ -443,29 +589,99 @@ static int midas_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); card->dev = dev; - priv->reg_mic_bias = devm_regulator_get(dev, "mic-bias"); - if (IS_ERR(priv->reg_mic_bias)) { - dev_err(dev, "Failed to get mic bias regulator\n"); - return PTR_ERR(priv->reg_mic_bias); - } - - priv->reg_submic_bias = devm_regulator_get(dev, "submic-bias"); - if (IS_ERR(priv->reg_submic_bias)) { - dev_err(dev, "Failed to get submic bias regulator\n"); - return PTR_ERR(priv->reg_submic_bias); - } - priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpio_fm_sel)) { - dev_err(dev, "Failed to get FM selection GPIO\n"); - return PTR_ERR(priv->gpio_fm_sel); - } + if (IS_ERR(priv->gpio_fm_sel)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_fm_sel), + "Failed to get FM selection GPIO\n"); priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpio_lineout_sel)) { - dev_err(dev, "Failed to get line out selection GPIO\n"); - return PTR_ERR(priv->gpio_lineout_sel); + if (IS_ERR(priv->gpio_lineout_sel)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_lineout_sel), + "Failed to get line out selection GPIO\n"); + + priv->gpio_headset_detect = devm_gpiod_get_optional(dev, + "headset-detect", GPIOD_IN); + if (IS_ERR(priv->gpio_headset_detect)) + return dev_err_probe(dev, PTR_ERR(priv->gpio_headset_detect), + "Failed to get headset jack detect GPIO\n"); + + if (priv->gpio_headset_detect) { + priv->adc_headset_detect = devm_iio_channel_get(dev, + "headset-detect"); + if (IS_ERR(priv->adc_headset_detect)) + return dev_err_probe(dev, + PTR_ERR(priv->adc_headset_detect), + "Failed to get ADC channel\n"); + + ret = iio_get_channel_type(priv->adc_headset_detect, + &channel_type); + if (ret) { + dev_err(dev, "Failed to get ADC channel type\n"); + return ret; + } + + if (channel_type != IIO_VOLTAGE) { + dev_err(dev, "ADC channel is not voltage\n"); + return -EINVAL; + } + + priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key", + GPIOD_IN); + if (IS_ERR(priv->gpio_headset_key)) + return dev_err_probe(dev, + PTR_ERR(priv->gpio_headset_key), + "Failed to get headset key GPIO\n"); + + ret = of_property_read_u32_array(dev->of_node, + "samsung,headset-4pole-threshold-microvolt", + fourpole_threshold, + ARRAY_SIZE(fourpole_threshold)); + if (ret) { + dev_err(dev, "Failed to get 4-pole jack detection threshold\n"); + return ret; + } + + if (fourpole_threshold[0] > fourpole_threshold[1]) { + dev_err(dev, "Invalid 4-pole jack detection threshold value\n"); + return -EINVAL; + } + + headset_jack_zones[0].max_mv = (fourpole_threshold[0]); + headset_jack_zones[1].min_mv = (fourpole_threshold[0] + 1); + + headset_jack_zones[1].max_mv = (fourpole_threshold[1]); + headset_jack_zones[2].min_mv = (fourpole_threshold[1] + 1); + + ret = of_property_read_u32_array(dev->of_node, + "samsung,headset-button-threshold-microvolt", + button_threshold, + ARRAY_SIZE(button_threshold)); + if (ret) { + dev_err(dev, "Failed to get headset button detection threshold\n"); + return ret; + } + + if (button_threshold[0] > button_threshold[1] || + button_threshold[1] > button_threshold[2]) { + dev_err(dev, "Invalid headset button detection threshold value\n"); + return -EINVAL; + } + + for (i = 0; i < 3; i++) { + if (i != 0 && button_threshold[i] <= 0) { + dev_err(dev, "Invalid headset button detection threshold value\n"); + return -EINVAL; + } + + headset_key_zones[i].min_mv = button_threshold[i]; + + if (i == 2) + headset_key_zones[i].max_mv = UINT_MAX; + else + headset_key_zones[i].max_mv = \ + (button_threshold[i+1] - 1); + } } ret = snd_soc_of_parse_card_name(card, "model"); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 84601ba43b7d..087e379aa3bc 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1713,7 +1713,7 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 fsi_dai_formats = +static const u64 fsi_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_NB_NF | diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6bc7027ed4db..63b3c8bf0fde 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1061,7 +1061,7 @@ static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream, return rsnd_dai_call(prepare, io, priv); } -static u64 rsnd_soc_dai_formats[] = { +static const u64 rsnd_soc_dai_formats[] = { /* * 1st Priority * diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3ab6626ad680..724fe1f033b5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -297,7 +297,7 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, return 0; } -const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) +const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai) { /* see snd_soc_is_matching_dai() */ if (dai->driver->name) @@ -3430,7 +3430,7 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_clock_provider_raw); -int snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream) +int snd_soc_get_stream_cpu(const struct snd_soc_dai_link *dai_link, int stream) { /* * [Normal] diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index fefe394dce72..9e47053419c1 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -11,7 +11,7 @@ #include <sound/soc-link.h> #define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret) -static inline int _soc_dai_ret(struct snd_soc_dai *dai, +static inline int _soc_dai_ret(const struct snd_soc_dai *dai, const char *func, int ret) { /* Positive, Zero values are not errors */ @@ -134,7 +134,7 @@ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); -int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd) +int snd_soc_dai_get_fmt_max_priority(const struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai; int i, max = 0; @@ -166,7 +166,7 @@ int snd_soc_dai_get_fmt_max_priority(struct snd_soc_pcm_runtime *rtd) * modes. This will mean that sometimes fewer formats * are reported here than are supported by set_fmt(). */ -u64 snd_soc_dai_get_fmt(struct snd_soc_dai *dai, int priority) +u64 snd_soc_dai_get_fmt(const struct snd_soc_dai *dai, int priority) { const struct snd_soc_dai_ops *ops = dai->driver->ops; u64 fmt = 0; @@ -304,8 +304,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); * configure the relationship between channel number and TDM slot number. */ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) + unsigned int tx_num, const unsigned int *tx_slot, + unsigned int rx_num, const unsigned int *rx_slot) { int ret = -ENOTSUPP; @@ -327,7 +327,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); * @rx_slot: pointer to an array which imply the RX slot number channel * 0~num-1 uses */ -int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, +int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { @@ -471,9 +471,9 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, * * Returns true if the DAI supports the indicated stream type. */ -bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) +bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int dir) { - struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); + const struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); /* If the codec specifies any channels at all, it supports the stream */ return stream->channels_min; @@ -528,7 +528,7 @@ void snd_soc_dai_action(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(snd_soc_dai_action); -int snd_soc_dai_active(struct snd_soc_dai *dai) +int snd_soc_dai_active(const struct snd_soc_dai *dai) { int stream, active; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 16dad4a45443..37dccd9c1ba0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/async.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/bitops.h> @@ -323,9 +324,9 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( const struct snd_soc_dapm_widget *_widget, const char *prefix) { - struct snd_soc_dapm_widget *w; - - w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); + struct snd_soc_dapm_widget *w __free(kfree) = kmemdup(_widget, + sizeof(*_widget), + GFP_KERNEL); if (!w) return NULL; @@ -333,20 +334,18 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, _widget->name); else w->name = kstrdup_const(_widget->name, GFP_KERNEL); - if (!w->name) { - kfree(w); + if (!w->name) return NULL; - } if (_widget->sname) { w->sname = kstrdup_const(_widget->sname, GFP_KERNEL); if (!w->sname) { kfree_const(w->name); - kfree(w); return NULL; } } - return w; + + return_ptr(w); } struct dapm_kcontrol_data { @@ -3857,7 +3856,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); */ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget, - int num) + unsigned int num) { int i; int ret = 0; @@ -3883,11 +3882,10 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_path *path; struct snd_soc_dai *source, *sink; struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_pcm_hw_params *params = NULL; const struct snd_soc_pcm_stream *config = NULL; struct snd_pcm_runtime *runtime = NULL; unsigned int fmt; - int ret = 0; + int ret; /* * NOTE @@ -3898,15 +3896,14 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, * stuff that increases stack usage. * So, we use kzalloc()/kfree() for params in this function. */ - params = kzalloc(sizeof(*params), GFP_KERNEL); + struct snd_pcm_hw_params *params __free(kfree) = kzalloc(sizeof(*params), + GFP_KERNEL); if (!params) return -ENOMEM; runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (!runtime) { - ret = -ENOMEM; - goto out; - } + if (!runtime) + return -ENOMEM; substream->runtime = runtime; @@ -3916,7 +3913,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_startup(source, substream); if (ret < 0) - goto out; + return ret; snd_soc_dai_activate(source, substream->stream); } @@ -3927,7 +3924,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_startup(sink, substream); if (ret < 0) - goto out; + return ret; snd_soc_dai_activate(sink, substream->stream); } @@ -3942,16 +3939,14 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, config = rtd->dai_link->c2c_params + rtd->c2c_params_select; if (!config) { dev_err(w->dapm->dev, "ASoC: link config missing\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } /* Be a little careful as we don't want to overflow the mask array */ if (!config->formats) { dev_warn(w->dapm->dev, "ASoC: Invalid format was specified\n"); - ret = -EINVAL; - goto out; + return -EINVAL; } fmt = ffs(config->formats) - 1; @@ -3972,7 +3967,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_hw_params(source, substream, params); if (ret < 0) - goto out; + return ret; dapm_update_dai_unlocked(substream, params, source); } @@ -3983,7 +3978,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_hw_params(sink, substream, params); if (ret < 0) - goto out; + return ret; dapm_update_dai_unlocked(substream, params, sink); } @@ -3993,11 +3988,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, runtime->channels = params_channels(params); runtime->rate = params_rate(params); -out: - /* see above NOTE */ - kfree(params); - - return ret; + return 0; } static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index b27e89ff6a16..19928f098d8d 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -11,6 +11,7 @@ // with code, comments and ideas from :- // Richard Purdie <richard@openedhand.com> +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -727,14 +728,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes *params = (void *)kcontrol->private_value; int ret, len; unsigned int val, mask; - void *data; if (!component->regmap || !params->num_regs) return -EINVAL; len = params->num_regs * component->val_bytes; - data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + void *data __free(kfree) = kmemdup(ucontrol->value.bytes.data, len, + GFP_KERNEL | GFP_DMA); if (!data) return -ENOMEM; @@ -746,7 +747,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, if (params->mask) { ret = regmap_read(component->regmap, params->base, &val); if (ret != 0) - goto out; + return ret; val &= params->mask; @@ -760,14 +761,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) - goto out; + return ret; ((u16 *)data)[0] &= mask; ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) - goto out; + return ret; ((u16 *)data)[0] |= val; break; @@ -776,30 +777,23 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) - goto out; + return ret; ((u32 *)data)[0] &= mask; ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) - goto out; + return ret; ((u32 *)data)[0] |= val; break; default: - ret = -EINVAL; - goto out; + return -EINVAL; } } - ret = regmap_raw_write(component->regmap, params->base, - data, len); - -out: - kfree(data); - - return ret; + return regmap_raw_write(component->regmap, params->base, data, len); } EXPORT_SYMBOL_GPL(snd_soc_bytes_put); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 711b2f49ed88..bad823718ae4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -504,7 +504,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) unsigned int bits = 0, cpu_bits = 0; for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); + const struct snd_soc_pcm_stream *pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream); if (pcm_codec->sig_bits == 0) { bits = 0; @@ -514,7 +514,7 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); + const struct snd_soc_pcm_stream *pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream); if (pcm_cpu->sig_bits == 0) { cpu_bits = 0; @@ -538,7 +538,7 @@ static void soc_pcm_hw_init(struct snd_pcm_hardware *hw) } static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->rates = snd_pcm_rate_mask_intersect(hw->rates, p->rates); @@ -551,20 +551,20 @@ static void soc_pcm_hw_update_rate(struct snd_pcm_hardware *hw, } static void soc_pcm_hw_update_chan(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->channels_min = max(hw->channels_min, p->channels_min); hw->channels_max = min(hw->channels_max, p->channels_max); } static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->formats &= p->formats; } static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw, - struct snd_soc_pcm_stream *p) + const struct snd_soc_pcm_stream *p) { hw->subformats &= p->subformats; } @@ -583,8 +583,8 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; - struct snd_soc_pcm_stream *codec_stream; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *cpu_stream; unsigned int cpu_chan_min = 0, cpu_chan_max = UINT_MAX; int i; @@ -1712,7 +1712,7 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) hw->formats &= formats; for_each_rtd_cpu_dais(fe, i, dai) { - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; /* * Skip CPUs which don't support the current stream @@ -1750,7 +1750,7 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *codec_stream; + const struct snd_soc_pcm_stream *codec_stream; int i; for_each_rtd_codec_dais(be, i, dai) { @@ -1787,7 +1787,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *cpu_stream; + const struct snd_soc_pcm_stream *cpu_stream; struct snd_soc_dai *dai; int i; @@ -1809,7 +1809,7 @@ static void dpcm_runtime_setup_be_chan(struct snd_pcm_substream *substream) * DAIs connected to a single CPU DAI, use CPU DAI's directly */ if (be->dai_link->num_codecs == 1) { - struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( + const struct snd_soc_pcm_stream *codec_stream = snd_soc_dai_get_pcm_stream( snd_soc_rtd_to_codec(be, 0), stream); soc_pcm_hw_update_chan(hw, codec_stream); @@ -1835,7 +1835,7 @@ static void dpcm_runtime_setup_be_rate(struct snd_pcm_substream *substream) for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_pcm_stream *pcm; + const struct snd_soc_pcm_stream *pcm; struct snd_soc_dai *dai; int i; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6951ff7bc61e..af5d42b57be7 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -73,7 +73,7 @@ struct soc_tplg { int bytes_ext_ops_count; /* optional fw loading callbacks to component drivers */ - struct snd_soc_tplg_ops *ops; + const struct snd_soc_tplg_ops *ops; }; /* check we dont overflow the data for this control chunk */ @@ -394,13 +394,9 @@ static void soc_tplg_remove_widget(struct snd_soc_component *comp, if (dobj->unload) dobj->unload(comp, dobj); - if (!w->kcontrols) - goto free_news; - - for (i = 0; w->kcontrols && i < w->num_kcontrols; i++) - snd_ctl_remove(card, w->kcontrols[i]); - -free_news: + if (w->kcontrols) + for (i = 0; i < w->num_kcontrols; i++) + snd_ctl_remove(card, w->kcontrols[i]); list_del(&dobj->list); @@ -644,105 +640,33 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg, return 0; } -static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) -{ - struct snd_soc_tplg_bytes_control *be; - struct soc_bytes_ext *sbe; - struct snd_kcontrol_new kc; - int ret = 0; - - if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_bytes_control), - 1, size, "mixer bytes")) - return -EINVAL; - - be = (struct snd_soc_tplg_bytes_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); - if (sbe == NULL) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + - le32_to_cpu(be->priv.size)); - - dev_dbg(tplg->dev, - "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); - - memset(&kc, 0, sizeof(kc)); - kc.name = be->hdr.name; - kc.private_value = (long)sbe; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(be->hdr.access); - - sbe->max = le32_to_cpu(be->max); - sbe->dobj.type = SND_SOC_DOBJ_BYTES; - if (tplg->ops) - sbe->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&sbe->dobj.list); - - /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); - if (ret) { - soc_control_err(tplg, &be->hdr, be->hdr.name); - goto err; - } - - /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &be->hdr); - if (ret < 0) - goto err; - - /* register control here */ - ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); - if (ret < 0) - goto err; - - list_add(&sbe->dobj.list, &tplg->comp->dobj_list); - -err: - return ret; -} - -static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) +static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { struct snd_soc_tplg_mixer_control *mc; struct soc_mixer_control *sm; - struct snd_kcontrol_new kc; - int ret = 0; - - if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_mixer_control), - 1, size, "mixers")) - return -EINVAL; + int err; mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); - if (sm == NULL) + if (!sm) return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + - le32_to_cpu(mc->priv.size)); - dev_dbg(tplg->dev, - "ASoC: adding mixer kcontrol %s with access 0x%x\n", + tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + le32_to_cpu(mc->priv.size); + + dev_dbg(tplg->dev, "ASoC: adding mixer kcontrol %s with access 0x%x\n", mc->hdr.name, mc->hdr.access); - memset(&kc, 0, sizeof(kc)); - kc.name = mc->hdr.name; - kc.private_value = (long)sm; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(mc->hdr.access); + kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)sm; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(mc->hdr.access); /* we only support FL/FR channel mapping atm */ sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); @@ -754,40 +678,23 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) sm->min = le32_to_cpu(mc->min); sm->invert = le32_to_cpu(mc->invert); sm->platform_max = le32_to_cpu(mc->platform_max); - sm->dobj.index = tplg->index; - sm->dobj.type = SND_SOC_DOBJ_MIXER; - if (tplg->ops) - sm->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); - if (ret) { + err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); + if (err) { soc_control_err(tplg, &mc->hdr, mc->hdr.name); - goto err; + return err; } /* create any TLV data */ - ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); - if (ret < 0) { + err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); + if (err < 0) { dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); - goto err; + return err; } /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &mc->hdr); - if (ret < 0) - goto err; - - /* register control here */ - ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); - if (ret < 0) - goto err; - - list_add(&sm->dobj.list, &tplg->comp->dobj_list); - -err: - return ret; + return soc_tplg_control_load(tplg, kc, &mc->hdr); } static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se, @@ -851,107 +758,220 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum * se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); } + se->items = le32_to_cpu(ec->items); + se->values = (const unsigned int *)se->dobj.control.dvalues; return 0; } -static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) +static int soc_tplg_control_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) { struct snd_soc_tplg_enum_control *ec; struct soc_enum *se; - struct snd_kcontrol_new kc; - int ret = 0; - - if (soc_tplg_check_elem_count(tplg, - sizeof(struct snd_soc_tplg_enum_control), - 1, size, "enums")) - return -EINVAL; + int err; ec = (struct snd_soc_tplg_enum_control *)tplg->pos; /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) return -EINVAL; - se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL); - if (se == NULL) + se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); + if (!se) return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - le32_to_cpu(ec->priv.size)); + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + le32_to_cpu(ec->priv.size)); - dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", - ec->hdr.name, ec->items); + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", ec->hdr.name, ec->items); - memset(&kc, 0, sizeof(kc)); - kc.name = ec->hdr.name; - kc.private_value = (long)se; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(ec->hdr.access); + kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(ec->hdr.access); + /* we only support FL/FR channel mapping atm */ se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); - se->shift_r = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); + se->shift_l = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_r = tplg_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); se->mask = le32_to_cpu(ec->mask); - se->dobj.index = tplg->index; - se->dobj.type = SND_SOC_DOBJ_ENUM; - if (tplg->ops) - se->dobj.unload = tplg->ops->control_unload; - INIT_LIST_HEAD(&se->dobj.list); switch (le32_to_cpu(ec->hdr.ops.info)) { - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: case SND_SOC_TPLG_CTL_ENUM_VALUE: - ret = soc_tplg_denum_create_values(tplg, se, ec); - if (ret < 0) { - dev_err(tplg->dev, - "ASoC: could not create values for %s\n", - ec->hdr.name); - goto err; + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(tplg, se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create values for %s\n", ec->hdr.name); + return err; } fallthrough; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - ret = soc_tplg_denum_create_texts(tplg, se, ec); - if (ret < 0) { - dev_err(tplg->dev, - "ASoC: could not create texts for %s\n", - ec->hdr.name); - goto err; + err = soc_tplg_denum_create_texts(tplg, se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", ec->hdr.name); + return err; } break; default: - ret = -EINVAL; - dev_err(tplg->dev, - "ASoC: invalid enum control type %d for %s\n", + dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", ec->hdr.ops.info, ec->hdr.name); - goto err; + return -EINVAL; } /* map io handlers */ - ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); - if (ret) { + err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); + if (err) { soc_control_err(tplg, &ec->hdr, ec->hdr.name); - goto err; + return err; + } + + /* pass control to driver for optional further init */ + return soc_tplg_control_load(tplg, kc, &ec->hdr); +} + +static int soc_tplg_control_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) +{ + struct snd_soc_tplg_bytes_control *be; + struct soc_bytes_ext *sbe; + int err; + + be = (struct snd_soc_tplg_bytes_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); + if (!sbe) + return -ENOMEM; + + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + le32_to_cpu(be->priv.size)); + + dev_dbg(tplg->dev, "ASoC: adding bytes kcontrol %s with access 0x%x\n", + be->hdr.name, be->hdr.access); + + kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + kc->private_value = (long)sbe; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = le32_to_cpu(be->hdr.access); + + sbe->max = le32_to_cpu(be->max); + + /* map standard io handlers and check for external handlers */ + err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); + if (err) { + soc_control_err(tplg, &be->hdr, be->hdr.name); + return err; } /* pass control to driver for optional further init */ - ret = soc_tplg_control_load(tplg, &kc, &ec->hdr); + return soc_tplg_control_load(tplg, kc, &be->hdr); +} + +static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) +{ + struct snd_kcontrol_new kc = {0}; + struct soc_bytes_ext *sbe; + int ret; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_bytes_control), + 1, size, "mixer bytes")) + return -EINVAL; + + ret = soc_tplg_control_dbytes_create(tplg, &kc); + if (ret) + return ret; + + /* register dynamic object */ + sbe = (struct soc_bytes_ext *)&kc.private_value; + + INIT_LIST_HEAD(&sbe->dobj.list); + sbe->dobj.type = SND_SOC_DOBJ_BYTES; + sbe->dobj.index = tplg->index; + if (tplg->ops) + sbe->dobj.unload = tplg->ops->control_unload; + + /* create control directly */ + ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); if (ret < 0) - goto err; + return ret; + + list_add(&sbe->dobj.list, &tplg->comp->dobj_list); - /* register control here */ + return ret; +} + +static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) +{ + struct snd_kcontrol_new kc = {0}; + struct soc_mixer_control *sm; + int ret; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_mixer_control), + 1, size, "mixers")) + return -EINVAL; + + ret = soc_tplg_control_dmixer_create(tplg, &kc); + if (ret) + return ret; + + /* register dynamic object */ + sm = (struct soc_mixer_control *)&kc.private_value; + + INIT_LIST_HEAD(&sm->dobj.list); + sm->dobj.type = SND_SOC_DOBJ_MIXER; + sm->dobj.index = tplg->index; + if (tplg->ops) + sm->dobj.unload = tplg->ops->control_unload; + + /* create control directly */ + ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); + if (ret < 0) + return ret; + + list_add(&sm->dobj.list, &tplg->comp->dobj_list); + + return ret; +} + +static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) +{ + struct snd_kcontrol_new kc = {0}; + struct soc_enum *se; + int ret; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_enum_control), + 1, size, "enums")) + return -EINVAL; + + ret = soc_tplg_control_denum_create(tplg, &kc); + if (ret) + return ret; + + /* register dynamic object */ + se = (struct soc_enum *)kc.private_value; + + INIT_LIST_HEAD(&se->dobj.list); + se->dobj.type = SND_SOC_DOBJ_ENUM; + se->dobj.index = tplg->index; + if (tplg->ops) + se->dobj.unload = tplg->ops->control_unload; + + /* create control directly */ ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol); if (ret < 0) - goto err; + return ret; list_add(&se->dobj.list, &tplg->comp->dobj_list); -err: return ret; } @@ -1094,206 +1114,6 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, return ret; } -static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct soc_mixer_control *sm; - struct snd_soc_tplg_mixer_control *mc; - int err; - - mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); - if (!sm) - return -ENOMEM; - - tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + - le32_to_cpu(mc->priv.size); - - dev_dbg(tplg->dev, " adding DAPM widget mixer control %s\n", - mc->hdr.name); - - kc->private_value = (long)sm; - kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(mc->hdr.access); - - /* we only support FL/FR channel mapping atm */ - sm->reg = tplg_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rreg = tplg_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FR); - sm->shift = tplg_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rshift = tplg_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FR); - - sm->max = le32_to_cpu(mc->max); - sm->min = le32_to_cpu(mc->min); - sm->invert = le32_to_cpu(mc->invert); - sm->platform_max = le32_to_cpu(mc->platform_max); - sm->dobj.index = tplg->index; - INIT_LIST_HEAD(&sm->dobj.list); - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &mc->hdr, mc->hdr.name); - return err; - } - - /* create any TLV data */ - err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", - mc->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &mc->hdr); - if (err < 0) - return err; - - return 0; -} - -static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct snd_soc_tplg_enum_control *ec; - struct soc_enum *se; - int err; - - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); - if (!se) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - le32_to_cpu(ec->priv.size)); - - dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", - ec->hdr.name); - - kc->private_value = (long)se; - kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(ec->hdr.access); - - /* we only support FL/FR channel mapping atm */ - se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); - se->shift_r = tplg_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FR); - - se->items = le32_to_cpu(ec->items); - se->mask = le32_to_cpu(ec->mask); - se->dobj.index = tplg->index; - - switch (le32_to_cpu(ec->hdr.ops.info)) { - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - err = soc_tplg_denum_create_values(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create values for %s\n", - ec->hdr.name); - return err; - } - fallthrough; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - err = soc_tplg_denum_create_texts(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not create texts for %s\n", - ec->hdr.name); - return err; - } - break; - default: - dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - return -EINVAL; - } - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &ec->hdr); - if (err < 0) - return err; - - return 0; -} - -static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) -{ - struct snd_soc_tplg_bytes_control *be; - struct soc_bytes_ext *sbe; - int err; - - be = (struct snd_soc_tplg_bytes_control *)tplg->pos; - - /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; - - sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); - if (!sbe) - return -ENOMEM; - - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + - le32_to_cpu(be->priv.size)); - - dev_dbg(tplg->dev, - "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); - - kc->private_value = (long)sbe; - kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); - if (!kc->name) - return -ENOMEM; - kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc->access = le32_to_cpu(be->hdr.access); - - sbe->max = le32_to_cpu(be->max); - INIT_LIST_HEAD(&sbe->dobj.list); - - /* map standard io handlers and check for external handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); - if (err) { - soc_control_err(tplg, &be->hdr, be->hdr.name); - return err; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_control_load(tplg, kc, &be->hdr); - if (err < 0) - return err; - - return 0; -} - static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, struct snd_soc_tplg_dapm_widget *w) { @@ -1381,7 +1201,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = mixer_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER; mixer_count++; - ret = soc_tplg_dapm_widget_dmixer_create(tplg, &kc[i]); + ret = soc_tplg_control_dmixer_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; @@ -1394,7 +1214,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = enum_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM; enum_count++; - ret = soc_tplg_dapm_widget_denum_create(tplg, &kc[i]); + ret = soc_tplg_control_denum_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; @@ -1403,7 +1223,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, kc[i].index = bytes_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES; bytes_count++; - ret = soc_tplg_dapm_widget_dbytes_create(tplg, &kc[i]); + ret = soc_tplg_control_dbytes_create(tplg, &kc[i]); if (ret < 0) goto hdr_err; break; @@ -2331,7 +2151,7 @@ static int soc_tplg_load(struct soc_tplg *tplg) /* load audio component topology from "firmware" file */ int snd_soc_tplg_component_load(struct snd_soc_component *comp, - struct snd_soc_tplg_ops *ops, const struct firmware *fw) + const struct snd_soc_tplg_ops *ops, const struct firmware *fw) { struct soc_tplg tplg; int ret; diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index d05e712c9518..303823dc45d7 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -21,7 +21,7 @@ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) } EXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); -int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) +int snd_soc_params_to_frame_size(const struct snd_pcm_hw_params *params) { int sample_size; @@ -40,7 +40,7 @@ int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) } EXPORT_SYMBOL_GPL(snd_soc_calc_bclk); -int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) +int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *params) { int ret; @@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); * Return: bclk frequency in Hz, else a negative error code if params format * is invalid. */ -int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, +int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params, int tdm_width, int tdm_slots, int slot_multiple) { if (!tdm_slots) @@ -144,7 +144,6 @@ static const struct snd_soc_component_driver dummy_codec = { .endianness = 1, }; -#define STUB_RATES SNDRV_PCM_RATE_8000_384000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ @@ -163,7 +162,7 @@ static const struct snd_soc_component_driver dummy_codec = { * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC */ -static u64 dummy_dai_formats = +static const u64 dummy_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | @@ -198,20 +197,24 @@ static struct snd_soc_dai_driver dummy_dai = { .stream_name = "Playback", .channels_min = 1, .channels_max = 384, - .rates = STUB_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 768000, .formats = STUB_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 384, - .rates = STUB_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5512, + .rate_max = 768000, .formats = STUB_FORMATS, }, .ops = &dummy_dai_ops, }; -int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) +int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai) { if (dai->driver == &dummy_dai) return 1; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index dead1c19558b..daf364f773dd 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -783,8 +783,8 @@ int hda_dsp_probe_early(struct snd_sof_dev *sdev) pci->class); return -ENODEV; } - dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", - pci->class); + dev_info_once(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", + pci->class); } chip = get_chip_info(sdev->pdata); diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index ebe1a7d16689..01db2e720b44 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -183,7 +183,7 @@ static const struct sof_dev_desc adl_desc = { .ops_free = hda_ops_free, }; -static const struct sof_dev_desc adl_n_desc = { +static const struct sof_dev_desc adln_desc = { .machines = snd_soc_acpi_intel_adl_machines, .alt_machines = snd_soc_acpi_intel_adl_sdw_machines, .use_acpi_target_states = true, @@ -298,7 +298,7 @@ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &rpl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &rpl_desc) }, - { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adln_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 32c7d1f3b528..be61e377e59e 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2500,7 +2500,7 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif return 0; } -static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +static int sof_ipc3_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { struct sof_dai_private_data *private = dai->private; @@ -2509,15 +2509,17 @@ static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *da switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: + switch (param_type) { + case SOF_DAI_PARAM_INTEL_SSP_MCLK: return private->dai_config->ssp.mclk_rate; - case SOF_DAI_CLK_INTEL_SSP_BCLK: + case SOF_DAI_PARAM_INTEL_SSP_BCLK: return private->dai_config->ssp.bclk_rate; + case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS: + return private->dai_config->ssp.tdm_slots; default: + dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; } - dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type); break; default: /* not yet implemented for platforms other than the above */ @@ -2692,7 +2694,7 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, - .dai_get_clk = sof_ipc3_dai_get_clk, + .dai_get_param = sof_ipc3_dai_get_param, .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, .parse_manifest = sof_ipc3_parse_manifest, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 00987039c972..90f6856ee80c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2869,7 +2869,7 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id, static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct snd_sof_widget *src_widget, struct snd_sof_widget *sink_widget, - int sink_id) + struct snd_sof_route *sroute) { struct sof_ipc4_copier_config_set_sink_format format; const struct sof_ipc_ops *iops = sdev->ipc->ops; @@ -2878,9 +2878,6 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct sof_ipc4_fw_module *fw_module; struct sof_ipc4_msg msg = {{ 0 }}; - dev_dbg(sdev->dev, "%s set copier sink %d format\n", - src_widget->widget->name, sink_id); - if (WIDGET_IS_DAI(src_widget->id)) { struct snd_sof_dai *dai = src_widget->private; @@ -2891,13 +2888,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, fw_module = src_widget->module_info; - format.sink_id = sink_id; + format.sink_id = sroute->src_queue_id; memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt)); - pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id); + pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id); if (!pin_fmt) { - dev_err(sdev->dev, "Unable to get pin %d format for %s", - sink_id, sink_widget->widget->name); + dev_err(sdev->dev, + "Failed to get input audio format of %s:%d for output of %s:%d\n", + sink_widget->widget->name, sroute->dst_queue_id, + src_widget->widget->name, sroute->src_queue_id); return -EINVAL; } @@ -2955,7 +2954,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { - dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n", + dev_err(sdev->dev, + "failed to get src_queue_id ID from source widget %s\n", src_widget->widget->name); return sroute->src_queue_id; } @@ -2963,7 +2963,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_INPUT); if (sroute->dst_queue_id < 0) { - dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n", + dev_err(sdev->dev, + "failed to get dst_queue_id ID from sink widget %s\n", sink_widget->widget->name); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); @@ -2972,10 +2973,11 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * /* Pin 0 format is already set during copier module init */ if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) { - ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, - sroute->src_queue_id); + ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, + sink_widget, sroute); if (ret < 0) { - dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n", + dev_err(sdev->dev, + "failed to set sink format for source %s:%d\n", src_widget->widget->name, sroute->src_queue_id); goto out; } @@ -3195,7 +3197,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, return 0; } -static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { struct sof_ipc4_copier *ipc4_copier = dai->private; struct snd_soc_tplg_hw_config *hw_config; @@ -3234,13 +3236,15 @@ static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *da switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: + switch (param_type) { + case SOF_DAI_PARAM_INTEL_SSP_MCLK: return le32_to_cpu(hw_config->mclk_rate); - case SOF_DAI_CLK_INTEL_SSP_BCLK: + case SOF_DAI_PARAM_INTEL_SSP_BCLK: return le32_to_cpu(hw_config->bclk_rate); + case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS: + return le32_to_cpu(hw_config->tdm_slots); default: - dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type); + dev_err(sdev->dev, "invalid SSP param %d\n", param_type); break; } break; @@ -3303,14 +3307,17 @@ static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link return 0; } -static enum sof_tokens common_copier_token_list[] = { +/* Tokens needed for different copier variants (aif, dai and buffer) */ +static enum sof_tokens copier_token_list[] = { SOF_COMP_TOKENS, + SOF_COPIER_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_DEEP_BUFFER_TOKENS, - SOF_COPIER_TOKENS, SOF_COMP_EXT_TOKENS, + + SOF_COPIER_DEEP_BUFFER_TOKENS, /* for AIF copier */ + SOF_DAI_TOKENS, /* for DAI copier */ }; static enum sof_tokens pipeline_token_list[] = { @@ -3318,16 +3325,6 @@ static enum sof_tokens pipeline_token_list[] = { SOF_PIPELINE_TOKENS, }; -static enum sof_tokens dai_token_list[] = { - SOF_COMP_TOKENS, - SOF_AUDIO_FMT_NUM_TOKENS, - SOF_IN_AUDIO_FORMAT_TOKENS, - SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_TOKENS, - SOF_DAI_TOKENS, - SOF_COMP_EXT_TOKENS, -}; - static enum sof_tokens pga_token_list[] = { SOF_COMP_TOKENS, SOF_GAIN_TOKENS, @@ -3364,23 +3361,23 @@ static enum sof_tokens process_token_list[] = { static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { [snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, - dai_token_list, ARRAY_SIZE(dai_token_list), NULL, + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, - dai_token_list, ARRAY_SIZE(dai_token_list), NULL, + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, - common_copier_token_list, ARRAY_SIZE(common_copier_token_list), + copier_token_list, ARRAY_SIZE(copier_token_list), NULL, sof_ipc4_prepare_copier_module, sof_ipc4_unprepare_copier_module}, [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, @@ -3417,7 +3414,7 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .route_free = sof_ipc4_route_free, .dai_config = sof_ipc4_dai_config, .parse_manifest = sof_ipc4_parse_manifest, - .dai_get_clk = sof_ipc4_dai_get_clk, + .dai_get_param = sof_ipc4_dai_get_param, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .link_setup = sof_ipc4_link_setup, }; diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index bea1b9d9ca28..74522400207e 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -82,7 +82,7 @@ static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc) } } -static struct mtk_adsp_ipc_ops dsp_ops = { +static const struct mtk_adsp_ipc_ops dsp_ops = { .handle_reply = mt8186_dsp_handle_reply, .handle_request = mt8186_dsp_handle_request, }; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 31dc98d1b1d8..24ae1d4959be 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -82,7 +82,7 @@ static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc) } } -static struct mtk_adsp_ipc_ops dsp_ops = { +static const struct mtk_adsp_ipc_ops dsp_ops = { .handle_reply = mt8195_dsp_handle_reply, .handle_request = mt8195_dsp_handle_request, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index ef9318947d74..9a52781bf8d8 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -978,7 +978,7 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, return NULL; } -static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) +static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); @@ -991,8 +991,8 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) if (!dai) return 0; - if (tplg_ops && tplg_ops->dai_get_clk) - return tplg_ops->dai_get_clk(sdev, dai, clk_type); + if (tplg_ops && tplg_ops->dai_get_param) + return tplg_ops->dai_get_param(sdev, dai, param_type); return 0; } @@ -1003,7 +1003,7 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) */ int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd) { - return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK); + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_MCLK); } EXPORT_SYMBOL(sof_dai_get_mclk); @@ -1013,6 +1013,16 @@ EXPORT_SYMBOL(sof_dai_get_mclk); */ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd) { - return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK); + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_BCLK); } EXPORT_SYMBOL(sof_dai_get_bclk); + +/* + * Helper to get SSP TDM slot number from a pcm_runtime. + * Return 0 if not exist. + */ +int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS); +} +EXPORT_SYMBOL(sof_dai_get_tdm_slots); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index ec2a3bb644d2..49be02234fc3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -44,8 +44,9 @@ #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer) -#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 -#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 +#define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 +#define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 +#define SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS 2 enum sof_widget_op { SOF_WIDGET_PREPARE, @@ -208,7 +209,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP - * @dai_get_clk: Function pointer for getting the DAI clock setting + * @dai_get_param: Function pointer for getting the DAI parameter * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest @@ -229,7 +230,7 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); - int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type); + int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*parse_manifest)(struct snd_soc_component *scomp, int index, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index da182314aa87..b54382131991 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2278,7 +2278,7 @@ static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_bytes_ext_volatile_get}, }; -static struct snd_soc_tplg_ops sof_tplg_ops = { +static const struct snd_soc_tplg_ops sof_tplg_ops = { /* external kcontrol init - used for any driver specific init */ .control_load = sof_control_load, .control_unload = sof_control_unload, @@ -2433,7 +2433,7 @@ static int sof_dspless_link_load(struct snd_soc_component *scomp, int index, return 0; } -static struct snd_soc_tplg_ops sof_dspless_tplg_ops = { +static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { /* external widget init - used for any driver specific init */ .widget_ready = sof_dspless_widget_ready, .widget_unload = sof_dspless_widget_unload, diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index ba7fdd7405ac..fe4fde844d86 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -8,11 +8,13 @@ #include <linux/device.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm_params.h> +#include <sound/simple_card_utils.h> #include <sound/soc.h> #include "tegra210_i2s.h" #include "tegra_cif.h" @@ -603,6 +605,7 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream, struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int sample_size, channels, srate, val, reg, path; struct tegra_cif_conf cif_conf; + snd_pcm_format_t sample_format; memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); @@ -615,28 +618,51 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream, cif_conf.audio_ch = channels; cif_conf.client_ch = channels; + if (i2s->client_channels) + cif_conf.client_ch = i2s->client_channels; + /* AHUB CIF Audio bits configs */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: + cif_conf.audio_bits = TEGRA_ACIF_BITS_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + dev_err(dev, "unsupported params audio bit format!\n"); + return -EOPNOTSUPP; + } + + sample_format = params_format(params); + if (i2s->client_sample_format >= 0) + sample_format = (snd_pcm_format_t)i2s->client_sample_format; + + /* + * Format of the I2S for sending/receiving the audio + * to/from external device. + */ + switch (sample_format) { + case SNDRV_PCM_FORMAT_S8: val = I2S_BITS_8; sample_size = 8; - cif_conf.audio_bits = TEGRA_ACIF_BITS_8; cif_conf.client_bits = TEGRA_ACIF_BITS_8; break; case SNDRV_PCM_FORMAT_S16_LE: val = I2S_BITS_16; sample_size = 16; - cif_conf.audio_bits = TEGRA_ACIF_BITS_16; cif_conf.client_bits = TEGRA_ACIF_BITS_16; break; case SNDRV_PCM_FORMAT_S32_LE: val = I2S_BITS_32; sample_size = 32; - cif_conf.audio_bits = TEGRA_ACIF_BITS_32; cif_conf.client_bits = TEGRA_ACIF_BITS_32; break; default: - dev_err(dev, "unsupported format!\n"); + dev_err(dev, "unsupported client bit format!\n"); return -EOPNOTSUPP; } @@ -872,6 +898,40 @@ static const struct regmap_config tegra210_i2s_regmap_config = { .cache_type = REGCACHE_FLAT, }; +/* + * The AHUB HW modules are interconnected with CIF which are capable of + * supporting Channel and Sample bit format conversion. This needs different + * CIF Audio and client configuration. As one of the config comes from + * params_channels() or params_format(), the extra configuration is passed from + * CIF Port of DT I2S node which can help to perform this conversion. + * + * 4ch audio = 4ch client = 2ch 2ch + * -----> ADMAIF -----------> CIF -------------> I2S ----> + */ +static void tegra210_parse_client_convert(struct device *dev) +{ + struct tegra210_i2s *i2s = dev_get_drvdata(dev); + struct device_node *ports, *ep; + struct simple_util_data data = {}; + int cif_port = 0; + + ports = of_get_child_by_name(dev->of_node, "ports"); + if (ports) { + ep = of_graph_get_endpoint_by_regs(ports, cif_port, -1); + if (ep) { + simple_util_parse_convert(ep, NULL, &data); + of_node_put(ep); + } + of_node_put(ports); + } + + if (data.convert_channels) + i2s->client_channels = data.convert_channels; + + if (data.convert_sample_format) + i2s->client_sample_format = simple_util_get_sample_fmt(&data); +} + static int tegra210_i2s_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -887,6 +947,7 @@ static int tegra210_i2s_probe(struct platform_device *pdev) i2s->tx_mask = DEFAULT_I2S_SLOT_MASK; i2s->rx_mask = DEFAULT_I2S_SLOT_MASK; i2s->loopback = false; + i2s->client_sample_format = -EINVAL; dev_set_drvdata(dev, i2s); @@ -916,6 +977,8 @@ static int tegra210_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->regmap); } + tegra210_parse_client_convert(dev); + regcache_cache_only(i2s->regmap, true); err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt, diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h index 030d70c45e18..fe478f3d8435 100644 --- a/sound/soc/tegra/tegra210_i2s.h +++ b/sound/soc/tegra/tegra210_i2s.h @@ -112,6 +112,8 @@ struct tegra210_i2s { struct clk *clk_i2s; struct clk *clk_sync_input; struct regmap *regmap; + int client_sample_format; + unsigned int client_channels; unsigned int stereo_to_mono[I2S_PATHS]; unsigned int mono_to_stereo[I2S_PATHS]; unsigned int dai_fmt; diff --git a/tools/arch/arm64/include/asm/cputype.h b/tools/arch/arm64/include/asm/cputype.h index 52f076afeb96..7b32b99023a2 100644 --- a/tools/arch/arm64/include/asm/cputype.h +++ b/tools/arch/arm64/include/asm/cputype.h @@ -86,6 +86,9 @@ #define ARM_CPU_PART_CORTEX_X2 0xD48 #define ARM_CPU_PART_NEOVERSE_N2 0xD49 #define ARM_CPU_PART_CORTEX_A78C 0xD4B +#define ARM_CPU_PART_NEOVERSE_V2 0xD4F +#define ARM_CPU_PART_CORTEX_X4 0xD82 +#define ARM_CPU_PART_NEOVERSE_V3 0xD84 #define APM_CPU_PART_XGENE 0x000 #define APM_CPU_VAR_POTENZA 0x00 @@ -159,6 +162,9 @@ #define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) #define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) #define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) +#define MIDR_NEOVERSE_V2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V2) +#define MIDR_CORTEX_X4 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X4) +#define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index e72c2b872957..e022e6eb766c 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -170,6 +170,10 @@ * CPU is not affected by Branch * History Injection. */ +#define ARCH_CAP_XAPIC_DISABLE BIT(21) /* + * IA32_XAPIC_DISABLE_STATUS MSR + * supported + */ #define ARCH_CAP_PBRSB_NO BIT(24) /* * Not susceptible to Post-Barrier * Return Stack Buffer Predictions. @@ -192,11 +196,6 @@ * File. */ -#define ARCH_CAP_XAPIC_DISABLE BIT(21) /* - * IA32_XAPIC_DISABLE_STATUS MSR - * supported - */ - #define MSR_IA32_FLUSH_CMD 0x0000010b #define L1D_FLUSH BIT(0) /* * Writeback and invalidate the diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index ef11aa4cab42..9fae1b73b529 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -457,8 +457,13 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 -/* attributes for system fd (group 0) */ -#define KVM_X86_XCOMP_GUEST_SUPP 0 +/* vendor-independent attributes for system fd (group 0) */ +#define KVM_X86_GRP_SYSTEM 0 +# define KVM_X86_XCOMP_GUEST_SUPP 0 + +/* vendor-specific groups and attributes for system fd */ +#define KVM_X86_GRP_SEV 1 +# define KVM_X86_SEV_VMSA_FEATURES 0 struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -689,6 +694,9 @@ enum sev_cmd_id { /* Guest Migration Extension */ KVM_SEV_SEND_CANCEL, + /* Second time is the charm; improved versions of the above ioctls. */ + KVM_SEV_INIT2, + KVM_SEV_NR_MAX, }; @@ -700,6 +708,14 @@ struct kvm_sev_cmd { __u32 sev_fd; }; +struct kvm_sev_init { + __u64 vmsa_features; + __u32 flags; + __u16 ghcb_version; + __u16 pad1; + __u32 pad2[8]; +}; + struct kvm_sev_launch_start { __u32 handle; __u32 policy; @@ -856,5 +872,7 @@ struct kvm_hyperv_eventfd { #define KVM_X86_DEFAULT_VM 0 #define KVM_X86_SW_PROTECTED_VM 1 +#define KVM_X86_SEV_VM 2 +#define KVM_X86_SEV_ES_VM 3 #endif /* _ASM_X86_KVM_H */ diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h index 75f00965ab15..d983c48a3b6a 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -842,8 +842,11 @@ __SYSCALL(__NR_lsm_set_self_attr, sys_lsm_set_self_attr) #define __NR_lsm_list_modules 461 __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) +#define __NR_mseal 462 +__SYSCALL(__NR_mseal, sys_mseal) + #undef __NR_syscalls -#define __NR_syscalls 462 +#define __NR_syscalls 463 /* * 32 bit systems traditionally used different diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/include/uapi/drm/i915_drm.h index 2ee338860b7e..d4d86e566e07 100644 --- a/tools/include/uapi/drm/i915_drm.h +++ b/tools/include/uapi/drm/i915_drm.h @@ -806,6 +806,12 @@ typedef struct drm_i915_irq_wait { */ #define I915_PARAM_PXP_STATUS 58 +/* + * Query if kernel allows marking a context to send a Freq hint to SLPC. This + * will enable use of the strategies allowed by the SLPC algorithm. + */ +#define I915_PARAM_HAS_CONTEXT_FREQ_HINT 59 + /* Must be kept compact -- no holes and well documented */ /** @@ -2148,6 +2154,15 @@ struct drm_i915_gem_context_param { * -EIO: The firmware did not succeed in creating the protected context. */ #define I915_CONTEXT_PARAM_PROTECTED_CONTENT 0xd + +/* + * I915_CONTEXT_PARAM_LOW_LATENCY: + * + * Mark this context as a low latency workload which requires aggressive GT + * frequency scaling. Use I915_PARAM_HAS_CONTEXT_FREQ_HINT to check if the kernel + * supports this per context flag. + */ +#define I915_CONTEXT_PARAM_LOW_LATENCY 0xe /* Must be kept compact -- no holes and well documented */ /** @value: Context parameter value to be set or queried */ @@ -2623,19 +2638,29 @@ struct drm_i915_reg_read { * */ +/* + * struct drm_i915_reset_stats - Return global reset and other context stats + * + * Driver keeps few stats for each contexts and also global reset count. + * This struct can be used to query those stats. + */ struct drm_i915_reset_stats { + /** @ctx_id: ID of the requested context */ __u32 ctx_id; + + /** @flags: MBZ */ __u32 flags; - /* All resets since boot/module reload, for all contexts */ + /** @reset_count: All resets since boot/module reload, for all contexts */ __u32 reset_count; - /* Number of batches lost when active in GPU, for this context */ + /** @batch_active: Number of batches lost when active in GPU, for this context */ __u32 batch_active; - /* Number of batches lost pending for execution, for this context */ + /** @batch_pending: Number of batches lost pending for execution, for this context */ __u32 batch_pending; + /** @pad: MBZ */ __u32 pad; }; diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index ea32b101b999..d03842abae57 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1221,9 +1221,9 @@ struct kvm_vfio_spapr_tce { /* Available with KVM_CAP_SPAPR_RESIZE_HPT */ #define KVM_PPC_RESIZE_HPT_PREPARE _IOR(KVMIO, 0xad, struct kvm_ppc_resize_hpt) #define KVM_PPC_RESIZE_HPT_COMMIT _IOR(KVMIO, 0xae, struct kvm_ppc_resize_hpt) -/* Available with KVM_CAP_PPC_RADIX_MMU or KVM_CAP_PPC_MMU_HASH_V3 */ +/* Available with KVM_CAP_PPC_MMU_RADIX or KVM_CAP_PPC_MMU_HASH_V3 */ #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) -/* Available with KVM_CAP_PPC_RADIX_MMU */ +/* Available with KVM_CAP_PPC_MMU_RADIX */ #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) /* Available with KVM_CAP_PPC_GET_CPU_CHAR */ #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) diff --git a/tools/include/uapi/linux/stat.h b/tools/include/uapi/linux/stat.h index 2f2ee82d5517..67626d535316 100644 --- a/tools/include/uapi/linux/stat.h +++ b/tools/include/uapi/linux/stat.h @@ -126,8 +126,9 @@ struct statx { __u64 stx_mnt_id; __u32 stx_dio_mem_align; /* Memory buffer alignment for direct I/O */ __u32 stx_dio_offset_align; /* File offset alignment for direct I/O */ + __u64 stx_subvol; /* Subvolume identifier */ /* 0xa0 */ - __u64 __spare3[12]; /* Spare space for future expansion */ + __u64 __spare3[11]; /* Spare space for future expansion */ /* 0x100 */ }; @@ -155,6 +156,7 @@ struct statx { #define STATX_MNT_ID 0x00001000U /* Got stx_mnt_id */ #define STATX_DIOALIGN 0x00002000U /* Want/got direct I/O alignment info */ #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ +#define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */ #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c index 3df0125ed5fa..50befe125ddc 100644 --- a/tools/lib/bpf/features.c +++ b/tools/lib/bpf/features.c @@ -393,7 +393,8 @@ static int probe_uprobe_multi_link(int token_fd) err = -errno; /* close() can clobber errno */ if (link_fd >= 0 || err != -EBADF) { - close(link_fd); + if (link_fd >= 0) + close(link_fd); close(prog_fd); return 0; } diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5c35c0d89306..e6d56b555369 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -214,6 +214,7 @@ NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help ifdef MAKECMDGOALS ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) + VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h config := 0 endif endif diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl index 532b855df589..1464c6be6eb3 100644 --- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl +++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl @@ -376,3 +376,4 @@ 459 n64 lsm_get_self_attr sys_lsm_get_self_attr 460 n64 lsm_set_self_attr sys_lsm_set_self_attr 461 n64 lsm_list_modules sys_lsm_list_modules +462 n64 mseal sys_mseal diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index 17173b82ca21..3656f1ca7a21 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -548,3 +548,4 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index 095bb86339a7..bd0fee24ad10 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -464,3 +464,4 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal sys_mseal diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index 7e8d46f4147f..a396f6e6ab5b 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -374,7 +374,7 @@ 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat 452 common fchmodat2 sys_fchmodat2 -453 64 map_shadow_stack sys_map_shadow_stack +453 common map_shadow_stack sys_map_shadow_stack 454 common futex_wake sys_futex_wake 455 common futex_wait sys_futex_wait 456 common futex_requeue sys_futex_requeue @@ -383,6 +383,7 @@ 459 common lsm_get_self_attr sys_lsm_get_self_attr 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal # # Due to a historical design error, certain syscalls are numbered differently diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 66a3de8ac661..0a8ba1323d64 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1956,8 +1956,7 @@ static void record__read_lost_samples(struct record *rec) if (count.lost) { if (!lost) { - lost = zalloc(sizeof(*lost) + - session->machines.host.id_hdr_size); + lost = zalloc(PERF_SAMPLE_MAX_SIZE); if (!lost) { pr_debug("Memory allocation failed\n"); return; @@ -1973,8 +1972,7 @@ static void record__read_lost_samples(struct record *rec) lost_count = perf_bpf_filter__lost_count(evsel); if (lost_count) { if (!lost) { - lost = zalloc(sizeof(*lost) + - session->machines.host.id_hdr_size); + lost = zalloc(PERF_SAMPLE_MAX_SIZE); if (!lost) { pr_debug("Memory allocation failed\n"); return; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 51eca671c797..08a3a6effac1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -765,7 +765,7 @@ static const char *fcntl_cmds[] = { static DEFINE_STRARRAY(fcntl_cmds, "F_"); static const char *fcntl_linux_specific_cmds[] = { - "SETLEASE", "GETLEASE", "NOTIFY", [5] = "CANCELLK", "DUPFD_CLOEXEC", + "SETLEASE", "GETLEASE", "NOTIFY", "DUPFD_QUERY", [5] = "CANCELLK", "DUPFD_CLOEXEC", "SETPIPE_SZ", "GETPIPE_SZ", "ADD_SEALS", "GET_SEALS", "GET_RW_HINT", "SET_RW_HINT", "GET_FILE_RW_HINT", "SET_FILE_RW_HINT", }; diff --git a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h index d18bfb238f66..13aea8fc3d45 100644 --- a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h +++ b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h @@ -97,10 +97,16 @@ #define LOCAL_TIMER_VECTOR 0xec +/* + * Posted interrupt notification vector for all device MSIs delivered to + * the host kernel. + */ +#define POSTED_MSI_NOTIFICATION_VECTOR 0xeb + #define NR_VECTORS 256 #ifdef CONFIG_X86_LOCAL_APIC -#define FIRST_SYSTEM_VECTOR LOCAL_TIMER_VECTOR +#define FIRST_SYSTEM_VECTOR POSTED_MSI_NOTIFICATION_VECTOR #else #define FIRST_SYSTEM_VECTOR NR_VECTORS #endif diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h index 139c330ccf2c..89d16b90370b 100644 --- a/tools/perf/trace/beauty/include/linux/socket.h +++ b/tools/perf/trace/beauty/include/linux/socket.h @@ -16,6 +16,7 @@ struct cred; struct socket; struct sock; struct sk_buff; +struct proto_accept_arg; #define __sockaddr_check_size(size) \ BUILD_BUG_ON(((size) > sizeof(struct __kernel_sockaddr_storage))) @@ -433,7 +434,7 @@ extern int __sys_recvfrom(int fd, void __user *ubuf, size_t size, extern int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, struct sockaddr __user *addr, int addr_len); -extern struct file *do_accept(struct file *file, unsigned file_flags, +extern struct file *do_accept(struct file *file, struct proto_accept_arg *arg, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, int flags); extern int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr, diff --git a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h index 282e90aeb163..c0bcc185fa48 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/fcntl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/fcntl.h @@ -9,6 +9,14 @@ #define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1) /* + * Request nofications on a directory. + * See below for events that may be notified. + */ +#define F_NOTIFY (F_LINUX_SPECIFIC_BASE + 2) + +#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3) + +/* * Cancel a blocking posix lock; internal use only until we expose an * asynchronous lock api to userspace: */ @@ -18,12 +26,6 @@ #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) /* - * Request nofications on a directory. - * See below for events that may be notified. - */ -#define F_NOTIFY (F_LINUX_SPECIFIC_BASE+2) - -/* * Set and get of pipe page size array */ #define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) diff --git a/tools/perf/trace/beauty/include/uapi/linux/prctl.h b/tools/perf/trace/beauty/include/uapi/linux/prctl.h index 370ed14b1ae0..35791791a879 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/prctl.h +++ b/tools/perf/trace/beauty/include/uapi/linux/prctl.h @@ -306,4 +306,26 @@ struct prctl_mm_map { # define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc # define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f +#define PR_RISCV_SET_ICACHE_FLUSH_CTX 71 +# define PR_RISCV_CTX_SW_FENCEI_ON 0 +# define PR_RISCV_CTX_SW_FENCEI_OFF 1 +# define PR_RISCV_SCOPE_PER_PROCESS 0 +# define PR_RISCV_SCOPE_PER_THREAD 1 + +/* PowerPC Dynamic Execution Control Register (DEXCR) controls */ +#define PR_PPC_GET_DEXCR 72 +#define PR_PPC_SET_DEXCR 73 +/* DEXCR aspect to act on */ +# define PR_PPC_DEXCR_SBHE 0 /* Speculative branch hint enable */ +# define PR_PPC_DEXCR_IBRTPD 1 /* Indirect branch recurrent target prediction disable */ +# define PR_PPC_DEXCR_SRAPD 2 /* Subroutine return address prediction disable */ +# define PR_PPC_DEXCR_NPHIE 3 /* Non-privileged hash instruction enable */ +/* Action to apply / return */ +# define PR_PPC_DEXCR_CTRL_EDITABLE 0x1 /* Aspect can be modified with PR_PPC_SET_DEXCR */ +# define PR_PPC_DEXCR_CTRL_SET 0x2 /* Set the aspect for this process */ +# define PR_PPC_DEXCR_CTRL_CLEAR 0x4 /* Clear the aspect for this process */ +# define PR_PPC_DEXCR_CTRL_SET_ONEXEC 0x8 /* Set the aspect on exec */ +# define PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC 0x10 /* Clear the aspect on exec */ +# define PR_PPC_DEXCR_CTRL_MASK 0x1f + #endif /* _LINUX_PRCTL_H */ diff --git a/tools/perf/trace/beauty/include/uapi/linux/stat.h b/tools/perf/trace/beauty/include/uapi/linux/stat.h index 2f2ee82d5517..67626d535316 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/stat.h +++ b/tools/perf/trace/beauty/include/uapi/linux/stat.h @@ -126,8 +126,9 @@ struct statx { __u64 stx_mnt_id; __u32 stx_dio_mem_align; /* Memory buffer alignment for direct I/O */ __u32 stx_dio_offset_align; /* File offset alignment for direct I/O */ + __u64 stx_subvol; /* Subvolume identifier */ /* 0xa0 */ - __u64 __spare3[12]; /* Spare space for future expansion */ + __u64 __spare3[11]; /* Spare space for future expansion */ /* 0x100 */ }; @@ -155,6 +156,7 @@ struct statx { #define STATX_MNT_ID 0x00001000U /* Got stx_mnt_id */ #define STATX_DIOALIGN 0x00002000U /* Want/got direct I/O alignment info */ #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ +#define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */ #define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */ diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index c519cc89c97f..0a56e22240fc 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -41,6 +41,16 @@ union core_pstate { unsigned res1:31; unsigned en:1; } pstatedef; + /* since fam 1Ah: */ + struct { + unsigned fid:12; + unsigned res1:2; + unsigned vid:8; + unsigned iddval:8; + unsigned idddiv:2; + unsigned res2:31; + unsigned en:1; + } pstatedef2; unsigned long long val; }; @@ -48,6 +58,10 @@ static int get_did(union core_pstate pstate) { int t; + /* Fam 1Ah onward do not use did */ + if (cpupower_cpu_info.family >= 0x1A) + return 0; + if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) t = pstate.pstatedef.did; else if (cpupower_cpu_info.family == 0x12) @@ -61,12 +75,18 @@ static int get_did(union core_pstate pstate) static int get_cof(union core_pstate pstate) { int t; - int fid, did, cof; + int fid, did, cof = 0; did = get_did(pstate); if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) { - fid = pstate.pstatedef.fid; - cof = 200 * fid / did; + if (cpupower_cpu_info.family >= 0x1A) { + fid = pstate.pstatedef2.fid; + if (fid > 0x0f) + cof = (fid * 5); + } else { + fid = pstate.pstatedef.fid; + cof = 200 * fid / did; + } } else { t = 0x10; fid = pstate.pstate.fid; diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 6584443144de..eaf091a3d331 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -3,6 +3,7 @@ #include <linux/platform_device.h> #include <linux/mod_devicetable.h> +#include <linux/vmalloc.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/sizes.h> diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile index 5af9ba8a4645..c1ce39874e2b 100644 --- a/tools/testing/selftests/alsa/Makefile +++ b/tools/testing/selftests/alsa/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # -CFLAGS += $(shell pkg-config --cflags alsa) +CFLAGS += $(shell pkg-config --cflags alsa) $(KHDR_INCLUDES) LDLIBS += $(shell pkg-config --libs alsa) ifeq ($(LDLIBS),) LDLIBS += -lasound diff --git a/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c b/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c index 02e718f06e0f..40531e56776e 100644 --- a/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c +++ b/tools/testing/selftests/bpf/progs/test_sk_storage_tracing.c @@ -84,7 +84,7 @@ int BPF_PROG(trace_tcp_connect, struct sock *sk) } SEC("fexit/inet_csk_accept") -int BPF_PROG(inet_csk_accept, struct sock *sk, int flags, int *err, bool kern, +int BPF_PROG(inet_csk_accept, struct sock *sk, struct proto_accept_arg *arg, struct sock *accepted_sk) { set_task_info(accepted_sk); diff --git a/tools/testing/selftests/cachestat/test_cachestat.c b/tools/testing/selftests/cachestat/test_cachestat.c index b171fd53b004..632ab44737ec 100644 --- a/tools/testing/selftests/cachestat/test_cachestat.c +++ b/tools/testing/selftests/cachestat/test_cachestat.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include <stdio.h> #include <stdbool.h> diff --git a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c index 759f86e7d263..2862aae58b79 100644 --- a/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c +++ b/tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include <inttypes.h> #include <unistd.h> diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index e59d985eeff0..048a312abf40 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -1,16 +1,28 @@ -CONFIG_KPROBES=y +CONFIG_BPF_SYSCALL=y +CONFIG_DEBUG_INFO_BTF=y +CONFIG_DEBUG_INFO_DWARF4=y +CONFIG_EPROBE_EVENTS=y +CONFIG_FPROBE=y +CONFIG_FPROBE_EVENTS=y CONFIG_FTRACE=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_FUNCTION_GRAPH_RETVAL=y CONFIG_FUNCTION_PROFILER=y -CONFIG_TRACER_SNAPSHOT=y -CONFIG_STACK_TRACER=y CONFIG_HIST_TRIGGERS=y -CONFIG_SCHED_TRACER=y -CONFIG_PREEMPT_TRACER=y CONFIG_IRQSOFF_TRACER=y -CONFIG_PREEMPTIRQ_DELAY_TEST=m +CONFIG_KALLSYMS_ALL=y +CONFIG_KPROBES=y +CONFIG_KPROBE_EVENTS=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y +CONFIG_PREEMPTIRQ_DELAY_TEST=m +CONFIG_PREEMPT_TRACER=y +CONFIG_PROBE_EVENTS_BTF_ARGS=y CONFIG_SAMPLES=y CONFIG_SAMPLE_FTRACE_DIRECT=m CONFIG_SAMPLE_TRACE_PRINTK=m -CONFIG_KALLSYMS_ALL=y +CONFIG_SCHED_TRACER=y +CONFIG_STACK_TRACER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_UPROBES=y +CONFIG_UPROBE_EVENTS=y diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc index d3a79da215c8..5f72abe6fa79 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/test_duplicates.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: Generic dynamic event - check if duplicate events are caught -# requires: dynamic_events "e[:[<group>/][<event>]] <attached-group>.<attached-event> [<args>]":README +# requires: dynamic_events "e[:[<group>/][<event>]] <attached-group>.<attached-event> [<args>]":README events/syscalls/sys_enter_openat echo 0 > events/enable diff --git a/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc index 3f74c09c56b6..118247b8dd84 100644 --- a/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc +++ b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc @@ -10,7 +10,6 @@ fail() { #msg } sample_events() { - echo > trace echo 1 > events/kmem/kmem_cache_free/enable echo 1 > tracing_on ls > /dev/null @@ -22,6 +21,7 @@ echo 0 > tracing_on echo 0 > events/enable echo "Get the most frequently calling function" +echo > trace sample_events target_func=`cat trace | grep -o 'call_site=\([^+]*\)' | sed 's/call_site=//' | sort | uniq -c | sort | tail -n 1 | sed 's/^[ 0-9]*//'` @@ -32,7 +32,16 @@ echo > trace echo "Test event filter function name" echo "call_site.function == $target_func" > events/kmem/kmem_cache_free/filter + +sample_events +max_retry=10 +while [ `grep kmem_cache_free trace| wc -l` -eq 0 ]; do sample_events +max_retry=$((max_retry - 1)) +if [ $max_retry -eq 0 ]; then + exit_fail +fi +done hitcnt=`grep kmem_cache_free trace| grep $target_func | wc -l` misscnt=`grep kmem_cache_free trace| grep -v $target_func | wc -l` @@ -49,7 +58,16 @@ address=`grep " ${target_func}\$" /proc/kallsyms | cut -d' ' -f1` echo "Test event filter function address" echo "call_site.function == 0x$address" > events/kmem/kmem_cache_free/filter +echo > trace +sample_events +max_retry=10 +while [ `grep kmem_cache_free trace| wc -l` -eq 0 ]; do sample_events +max_retry=$((max_retry - 1)) +if [ $max_retry -eq 0 ]; then + exit_fail +fi +done hitcnt=`grep kmem_cache_free trace| grep $target_func | wc -l` misscnt=`grep kmem_cache_free trace| grep -v $target_func | wc -l` diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc index 1f6981ef7afa..ba19b81cef39 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc @@ -30,7 +30,8 @@ find_dot_func() { fi grep " [tT] .*\.isra\..*" /proc/kallsyms | cut -f 3 -d " " | while read f; do - if grep -s $f available_filter_functions; then + cnt=`grep -s $f available_filter_functions | wc -l`; + if [ $cnt -eq 1 ]; then echo $f break fi diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index 11e157d7533b..78ab2cd111f6 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -3,8 +3,6 @@ SUBDIRS := functional TEST_PROGS := run.sh -.PHONY: all clean - include ../lib.mk all: diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile index a392d0917b4e..994fa3468f17 100644 --- a/tools/testing/selftests/futex/functional/Makefile +++ b/tools/testing/selftests/futex/functional/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 INCLUDES := -I../include -I../../ $(KHDR_INCLUDES) -CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE -pthread $(INCLUDES) $(KHDR_INCLUDES) +CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE= -pthread $(INCLUDES) $(KHDR_INCLUDES) LDLIBS := -lpthread -lrt LOCAL_HDRS := \ diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index 7f3ca5c78df1..215c6cb539b4 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -360,7 +360,7 @@ out: int main(int argc, char *argv[]) { - const char *test_name; + char *test_name; int c, ret; while ((c = getopt(argc, argv, "bchlot:v:")) != -1) { diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index ce8ff8e8ce3a..ac280dcba996 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -183,6 +183,7 @@ TEST_GEN_PROGS_s390x += s390x/sync_regs_test TEST_GEN_PROGS_s390x += s390x/tprot TEST_GEN_PROGS_s390x += s390x/cmma_test TEST_GEN_PROGS_s390x += s390x/debug_test +TEST_GEN_PROGS_s390x += s390x/shared_zeropage_test TEST_GEN_PROGS_s390x += demand_paging_test TEST_GEN_PROGS_s390x += dirty_log_test TEST_GEN_PROGS_s390x += guest_print_test diff --git a/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c new file mode 100644 index 000000000000..bba0d9a6dcc8 --- /dev/null +++ b/tools/testing/selftests/kvm/s390x/shared_zeropage_test.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test shared zeropage handling (with/without storage keys) + * + * Copyright (C) 2024, Red Hat, Inc. + */ +#include <sys/mman.h> + +#include <linux/fs.h> + +#include "test_util.h" +#include "kvm_util.h" +#include "kselftest.h" +#include "ucall_common.h" + +static void set_storage_key(void *addr, uint8_t skey) +{ + asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); +} + +static void guest_code(void) +{ + /* Issue some storage key instruction. */ + set_storage_key((void *)0, 0x98); + GUEST_DONE(); +} + +/* + * Returns 1 if the shared zeropage is mapped, 0 if something else is mapped. + * Returns < 0 on error or if nothing is mapped. + */ +static int maps_shared_zeropage(int pagemap_fd, void *addr) +{ + struct page_region region; + struct pm_scan_arg arg = { + .start = (uintptr_t)addr, + .end = (uintptr_t)addr + 4096, + .vec = (uintptr_t)®ion, + .vec_len = 1, + .size = sizeof(struct pm_scan_arg), + .category_mask = PAGE_IS_PFNZERO, + .category_anyof_mask = PAGE_IS_PRESENT, + .return_mask = PAGE_IS_PFNZERO, + }; + return ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); +} + +int main(int argc, char *argv[]) +{ + char *mem, *page0, *page1, *page2, tmp; + const size_t pagesize = getpagesize(); + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct ucall uc; + int pagemap_fd; + + ksft_print_header(); + ksft_set_plan(3); + + /* + * We'll use memory that is not mapped into the VM for simplicity. + * Shared zeropages are enabled/disabled per-process. + */ + mem = mmap(0, 3 * pagesize, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0); + TEST_ASSERT(mem != MAP_FAILED, "mmap() failed"); + + /* Disable THP. Ignore errors on older kernels. */ + madvise(mem, 3 * pagesize, MADV_NOHUGEPAGE); + + page0 = mem; + page1 = page0 + pagesize; + page2 = page1 + pagesize; + + /* Can we even detect shared zeropages? */ + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + TEST_REQUIRE(pagemap_fd >= 0); + + tmp = *page0; + asm volatile("" : "+r" (tmp)); + TEST_REQUIRE(maps_shared_zeropage(pagemap_fd, page0) == 1); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + /* Verify that we get the shared zeropage after VM creation. */ + tmp = *page1; + asm volatile("" : "+r" (tmp)); + ksft_test_result(maps_shared_zeropage(pagemap_fd, page1) == 1, + "Shared zeropages should be enabled\n"); + + /* + * Let our VM execute a storage key instruction that should + * unshare all shared zeropages. + */ + vcpu_run(vcpu); + get_ucall(vcpu, &uc); + TEST_ASSERT_EQ(uc.cmd, UCALL_DONE); + + /* Verify that we don't have a shared zeropage anymore. */ + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page1), + "Shared zeropage should be gone\n"); + + /* Verify that we don't get any new shared zeropages. */ + tmp = *page2; + asm volatile("" : "+r" (tmp)); + ksft_test_result(!maps_shared_zeropage(pagemap_fd, page2), + "Shared zeropages should be disabled\n"); + + kvm_vm_free(vm); + + ksft_finished(); +} diff --git a/tools/testing/selftests/net/hsr/config b/tools/testing/selftests/net/hsr/config index 22061204fb69..241542441c51 100644 --- a/tools/testing/selftests/net/hsr/config +++ b/tools/testing/selftests/net/hsr/config @@ -2,3 +2,4 @@ CONFIG_IPV6=y CONFIG_NET_SCH_NETEM=m CONFIG_HSR=y CONFIG_VETH=y +CONFIG_BRIDGE=y diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index edc030e81a46..9155c914c064 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -15,7 +15,7 @@ ksft_xfail=2 ksft_skip=4 # namespace list created by setup_ns -NS_LIST="" +NS_LIST=() ############################################################################## # Helpers @@ -27,6 +27,7 @@ __ksft_status_merge() local -A weights local weight=0 + local i for i in "$@"; do weights[$i]=$((weight++)) done @@ -67,9 +68,7 @@ loopy_wait() while true do local out - out=$("$@") - local ret=$? - if ((!ret)); then + if out=$("$@"); then echo -n "$out" return 0 fi @@ -139,6 +138,7 @@ cleanup_ns() fi for ns in "$@"; do + [ -z "${ns}" ] && continue ip netns delete "${ns}" &> /dev/null if ! busywait $BUSYWAIT_TIMEOUT ip netns list \| grep -vq "^$ns$" &> /dev/null; then echo "Warn: Failed to remove namespace $ns" @@ -152,7 +152,7 @@ cleanup_ns() cleanup_all_ns() { - cleanup_ns $NS_LIST + cleanup_ns "${NS_LIST[@]}" } # setup netns with given names as prefix. e.g @@ -161,7 +161,7 @@ setup_ns() { local ns="" local ns_name="" - local ns_list="" + local ns_list=() local ns_exist= for ns_name in "$@"; do # Some test may setup/remove same netns multi times @@ -177,13 +177,13 @@ setup_ns() if ! ip netns add "$ns"; then echo "Failed to create namespace $ns_name" - cleanup_ns "$ns_list" + cleanup_ns "${ns_list[@]}" return $ksft_skip fi ip -n "$ns" link set lo up - ! $ns_exist && ns_list="$ns_list $ns" + ! $ns_exist && ns_list+=("$ns") done - NS_LIST="$NS_LIST $ns_list" + NS_LIST+=("${ns_list[@]}") } tc_rule_stats_get() diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c index 9024754530b2..5790ab446527 100644 --- a/tools/testing/selftests/openat2/openat2_test.c +++ b/tools/testing/selftests/openat2/openat2_test.c @@ -5,6 +5,7 @@ */ #define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ // Use ll64 #include <fcntl.h> #include <sched.h> #include <sys/stat.h> |