sound updates for 6.19-rc1

The majority of changes at this time were about ASoC with a lot of
 code refactoring works.  From the functionality POV, there aren't much
 to see, but we have a wide range of device-specific fixes and updates.
 Here are some highlights:
 
 - Continued ASoC API clean works, spanned over many files
 - Added a SoundWire SCDA generic class driver with regmap support
 - Enhancements and fixes for Cirrus, Intel, Maxim and Qualcomm.
 - Support for ASoC Allwinner A523, Mediatek MT8189, Qualcomm QCM2290,
   QRB2210 and SM6115, SpacemiT K1, and TI TAS2568, TAS5802, TAS5806,
   TAS5815, TAS5828 and TAS5830
 - Usual HD-audio and USB-audio quirks and fixups
 - Support for Onkyo SE-300PCIE, TASCAM IF-FW/DM MkII
 
 Some gpiolib changes for shared GPIOs are included along with this PR
 for covering ASoC drivers changes.
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmkwQ2UOHHRpd2FpQHN1
 c2UuZGUACgkQLtJE4w1nLE8tIRAAjCHdIlMejNTCzGRlhsRSQVD6bo1wASXcjfJ6
 COH84akbnA0oT5z7H7JnzTOmfjzxLJpwC8j6IpZ/9CQazanT5IIVE41FZquXZ1JB
 RhQVzuGw9Pl4MaYVdFuRqIXjiP+msY1jpbo9/QXQo8D/B41wpmVTgzkFVW2rxPMy
 0aBOu4Wpu+11aBpNBy6dXDiKQ5kDqn7zOLoFGgcf5wlFIvOGZJ0Wg/i0kvCjl+ia
 xYiP+/F6xKOyTY1c98iqExbKzSSy4ddGFUwrkevm6bWpu8hkXiL1O0zMWOe769x6
 0wy0b5zvsbtOQOxbtK5+8gdjJw7ycgDa441hDtsaXBBROYZEV3D6+XZJCfq8Tz8F
 +vLH5lfZeLg+59eqt3GOMGlwBfuhH91qzukIYG3q9EQGOkNkZ19ySJnFMLom68Ei
 TCfNzh/ggSGXA9qAmfBcPoizgC/j9o+v4kbLRQteuRRWxES1FxqeN9Ba3d5JcHT3
 BQpz1bhUli73477D6voPcwXLiQlM+Alv4QUKTFr2nUnWUQKwMvkZFwiv2jTqVdDf
 f71Usv7xdyM7XijgmXuLg+3n0UvCwUPBB+bv3a1Bu7G4iTB1deNKU8t9k+sBJpcX
 aRs5ych3MiU/zG+KRMB5FEx31KzpKu+Kk9NQ207/1HLaNhTgD3cg2wS3T3qdRUPv
 Yf6wFHs=
 =1JUI
 -----END PGP SIGNATURE-----

Merge tag 'sound-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "The majority of changes at this time were about ASoC with a lot of
  code refactoring works. From the functionality POV, there isn't much
  to see, but we have a wide range of device-specific fixes and updates.
  Here are some highlights:

   - Continued ASoC API cleanup work, spanned over many files

   - Added a SoundWire SCDA generic class driver with regmap support

   - Enhancements and fixes for Cirrus, Intel, Maxim and Qualcomm.

   - Support for ASoC Allwinner A523, Mediatek MT8189, Qualcomm QCM2290,
     QRB2210 and SM6115, SpacemiT K1, and TI TAS2568, TAS5802, TAS5806,
     TAS5815, TAS5828 and TAS5830

   - Usual HD-audio and USB-audio quirks and fixups

   - Support for Onkyo SE-300PCIE, TASCAM IF-FW/DM MkII

  Some gpiolib changes for shared GPIOs are included along with this PR
  for covering ASoC drivers changes"

* tag 'sound-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (739 commits)
  ALSA: hda/realtek: Add PCI SSIDs to HP ProBook quirks
  ALSA: usb-audio: Simplify with usb_endpoint_max_periodic_payload()
  ALSA: hda/realtek: fix mute/micmute LEDs don't work for more HP laptops
  ALSA: rawmidi: Fix inconsistent indenting warning reported by smatch
  ALSA: dice: fix buffer overflow in detect_stream_formats()
  ASoC: codecs: Modify awinic amplifier dsp read and write functions
  ASoC: SDCA: Fixup some more Kconfig issues
  ASoC: cs35l56: Log a message if firmware is missing
  ASoC: nau8325: Delete a stray tab
  firmware: cs_dsp: Add test cases for client_ops == NULL
  firmware: cs_dsp: Don't require client to provide a struct cs_dsp_client_ops
  ASoC: fsl_micfil: Set channel range control
  ASoC: fsl_micfil: Add default quality for different platforms
  ASoC: intel: sof_sdw: Add codec_info for cs42l45
  ASoC: sdw_utils: Add cs42l45 support functions
  ASoC: intel: sof_sdw: Add ability to have auxiliary devices
  ASoC: sdw_utils: Move codec_name to dai info
  ASoC: sdw_utils: Add codec_conf for every DAI
  ASoC: SDCA: Add terminal type into input/output widget name
  ASoC: SDCA: Align mute controls to ALSA expectations
  ...
This commit is contained in:
Linus Torvalds 2025-12-04 10:08:40 -08:00
commit 2aa680df68
615 changed files with 33151 additions and 5681 deletions

View File

@ -1,60 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/adi,max98363.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices MAX98363 SoundWire Amplifier
maintainers:
- Ryan Lee <ryans.lee@analog.com>
description:
The MAX98363 is a SoundWire input Class D mono amplifier that
supports MIPI SoundWire v1.2-compatible digital interface for
audio and control data.
SoundWire peripheral device ID of MAX98363 is 0x3*019f836300
where * is the peripheral device unique ID decoded from pin.
It supports up to 10 peripheral devices(0x0 to 0x9).
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: sdw3019f836300
reg:
maxItems: 1
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
- "#sound-dai-cells"
unevaluatedProperties: false
examples:
- |
soundwire@3250000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x3250000 0x2000>;
speaker@0,0 {
compatible = "sdw3019f836300";
reg = <0 0>;
#sound-dai-cells = <0>;
sound-name-prefix = "Speaker Left";
};
speaker@0,1 {
compatible = "sdw3019f836300";
reg = <0 1>;
#sound-dai-cells = <0>;
sound-name-prefix = "Speaker Right";
};
};

View File

@ -1,19 +0,0 @@
Analog Devices SSM2602, SSM2603 and SSM2604 I2S audio CODEC devices
SSM2602 support both I2C and SPI as the configuration interface,
the selection is made by the MODE strap-in pin.
SSM2603 and SSM2604 only support I2C as the configuration interface.
Required properties:
- compatible : One of "adi,ssm2602", "adi,ssm2603" or "adi,ssm2604"
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
Example:
ssm2602: ssm2602@1a {
compatible = "adi,ssm2602";
reg = <0x1a>;
};

View File

@ -1,49 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/adi,ssm3515.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices SSM3515 Audio Amplifier
maintainers:
- Martin Povišer <povik+lin@cutebit.org>
description: |
SSM3515 is a mono Class-D audio amplifier with digital input.
https://www.analog.com/media/en/technical-documentation/data-sheets/SSM3515.pdf
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- adi,ssm3515
reg:
maxItems: 1
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@14 {
compatible = "adi,ssm3515";
reg = <0x14>;
#sound-dai-cells = <0>;
sound-name-prefix = "Left Tweeter";
};
};

View File

@ -33,7 +33,9 @@ properties:
- const: allwinner,sun50i-h6-i2s
- const: allwinner,sun50i-r329-i2s
- items:
- const: allwinner,sun20i-d1-i2s
- enum:
- allwinner,sun20i-d1-i2s
- allwinner,sun55i-a523-i2s
- const: allwinner,sun50i-r329-i2s
reg:

View File

@ -23,6 +23,7 @@ properties:
- const: allwinner,sun8i-h3-spdif
- const: allwinner,sun50i-h6-spdif
- const: allwinner,sun50i-h616-spdif
- const: allwinner,sun55i-a523-spdif
- items:
- const: allwinner,sun8i-a83t-spdif
- const: allwinner,sun8i-h3-spdif
@ -37,14 +38,12 @@ properties:
maxItems: 1
clocks:
items:
- description: Bus Clock
- description: Module Clock
minItems: 2
maxItems: 3
clock-names:
items:
- const: apb
- const: spdif
minItems: 2
maxItems: 3
# Even though it only applies to subschemas under the conditionals,
# not listing them here will trigger a warning because of the
@ -65,6 +64,7 @@ allOf:
- allwinner,sun8i-h3-spdif
- allwinner,sun50i-h6-spdif
- allwinner,sun50i-h616-spdif
- allwinner,sun55i-a523-spdif
then:
required:
@ -98,6 +98,38 @@ allOf:
- const: rx
- const: tx
- if:
properties:
compatible:
contains:
enum:
- allwinner,sun55i-a523-spdif
then:
properties:
clocks:
items:
- description: Bus Clock
- description: TX Clock
- description: RX Clock
clock-names:
items:
- const: apb
- const: tx
- const: rx
else:
properties:
clocks:
items:
- description: Bus Clock
- description: Module Clock
clock-names:
items:
- const: apb
- const: spdif
required:
- "#sound-dai-cells"
- compatible

View File

@ -25,6 +25,16 @@ properties:
reg:
maxItems: 1
clocks:
items:
- description:
Master clock connected to the MCLK pin if MCLK is an input (i.e. no
crystal used).
clock-names:
items:
- const: mclk
spi-cpha: true
spi-cpol: true

View File

@ -15,10 +15,15 @@ description:
allOf:
- $ref: dai-common.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- cirrus,cs4282
- cirrus,cs4302
- cirrus,cs4304
- cirrus,cs4308
- cirrus,cs5302
- cirrus,cs5304
- cirrus,cs5308
@ -26,6 +31,9 @@ properties:
reg:
maxItems: 1
spi-max-frequency:
maximum: 24000000
'#sound-dai-cells':
const: 1

View File

@ -1,29 +0,0 @@
CS4265 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "cirrus,cs4265"
- reg : the I2C address of the device for I2C. The I2C address depends on
the state of the AD0 pin. If AD0 is high, the i2c address is 0x4f.
If it is low, the i2c address is 0x4e.
Optional properties:
- reset-gpios : a GPIO spec for the reset pin. If specified, it will be
deasserted before communication to the codec starts.
Examples:
codec_ad0_high: cs4265@4f { /* AD0 Pin is high */
compatible = "cirrus,cs4265";
reg = <0x4f>;
};
codec_ad0_low: cs4265@4e { /* AD0 Pin is low */
compatible = "cirrus,cs4265";
reg = <0x4e>;
};

View File

@ -1,22 +0,0 @@
Cirrus Logic CS4341 audio DAC
This device supports both I2C and SPI (configured with pin strapping
on the board).
Required properties:
- compatible: "cirrus,cs4341a"
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
For required properties on I2C-bus, please consult
dtschema schemas/i2c/i2c-controller.yaml
For required properties on SPI-bus, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt
Example:
codec: cs4341@0 {
#sound-dai-cells = <0>;
compatible = "cirrus,cs4341a";
reg = <0>;
spi-max-frequency = <6000000>;
};

View File

@ -1,19 +0,0 @@
CS4349 audio CODEC
Required properties:
- compatible : "cirrus,cs4349"
- reg : the I2C address of the device for I2C
Optional properties:
- reset-gpios : a GPIO spec for the reset pin.
Example:
codec: cs4349@48 {
compatible = "cirrus,cs4349";
reg = <0x48>;
reset-gpios = <&gpio 54 0>;
};

View File

@ -1,22 +0,0 @@
* Dialog DA9055 Audio CODEC
DA9055 provides Audio CODEC support (I2C only).
The Audio CODEC device in DA9055 has its own I2C address which is configurable,
so the device is instantiated separately from the PMIC (MFD) device.
For details on accompanying PMIC I2C device, see the following:
Documentation/devicetree/bindings/mfd/da9055.txt
Required properties:
- compatible: "dlg,da9055-codec"
- reg: Specifies the I2C slave address
Example:
codec: da9055-codec@1a {
compatible = "dlg,da9055-codec";
reg = <0x1a>;
};

View File

@ -0,0 +1,178 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/mediatek,mt8189-afe-pcm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek Audio Front End PCM controller for MT8189
maintainers:
- Darren Ye <darren.ye@mediatek.com>
- Cyril Chao <cyril.chao@mediatek.com>
properties:
compatible:
const: mediatek,mt8189-afe-pcm
reg:
maxItems: 1
interrupts:
maxItems: 1
memory-region:
maxItems: 1
mediatek,apmixedsys:
$ref: /schemas/types.yaml#/definitions/phandle
description: To set up the apll12 tuner
power-domains:
maxItems: 1
clocks:
items:
- description: mux for audio intbus
- description: mux for audio engen1
- description: mux for audio engen2
- description: mux for audio h
- description: audio apll1 clock
- description: audio apll2 clock
- description: audio apll1 divide4
- description: audio apll2 divide4
- description: audio apll12 divide for i2sin0
- description: audio apll12 divide for i2sin1
- description: audio apll12 divide for i2sout0
- description: audio apll12 divide for i2sout1
- description: audio apll12 divide for fmi2s
- description: audio apll12 divide for tdmout mck
- description: audio apll12 divide for tdmout bck
- description: mux for audio apll1
- description: mux for audio apll2
- description: mux for i2sin0 mck
- description: mux for i2sin1 mck
- description: mux for i2sout0 mck
- description: mux for i2sout1 mck
- description: mux for fmi2s mck
- description: mux for tdmout mck
- description: 26m clock
- description: audio slv clock
- description: audio mst clock
- description: audio intbus clock
clock-names:
items:
- const: top_aud_intbus
- const: top_aud_eng1
- const: top_aud_eng2
- const: top_aud_h
- const: apll1
- const: apll2
- const: apll1_d4
- const: apll2_d4
- const: apll12_div_i2sin0
- const: apll12_div_i2sin1
- const: apll12_div_i2sout0
- const: apll12_div_i2sout1
- const: apll12_div_fmi2s
- const: apll12_div_tdmout_m
- const: apll12_div_tdmout_b
- const: top_apll1
- const: top_apll2
- const: top_i2sin0
- const: top_i2sin1
- const: top_i2sout0
- const: top_i2sout1
- const: top_fmi2s
- const: top_dptx
- const: clk26m
- const: aud_slv_ck_peri
- const: aud_mst_ck_peri
- const: aud_intbus_ck_peri
required:
- compatible
- reg
- interrupts
- memory-region
- power-domains
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
afe@11050000 {
compatible = "mediatek,mt8189-afe-pcm";
reg = <0 0x11050000 0 0x10000>;
interrupts = <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH 0>;
memory-region = <&afe_dma_mem_reserved>;
pinctrl-names = "default";
pinctrl-0 = <&aud_pins_default>;
power-domains = <&scpsys 1>; //MT8189_POWER_DOMAIN_AUDIO
clocks = <&topckgen_clk 23>, //CLK_TOP_AUD_INTBUS_SEL
<&topckgen_clk 39>, //CLK_TOP_AUD_ENGEN1_SEL
<&topckgen_clk 40>, //CLK_TOP_AUD_ENGEN2_SEL
<&topckgen_clk 49>, //CLK_TOP_AUDIO_H_SEL
<&topckgen_clk 146>, //CLK_TOP_APLL1
<&topckgen_clk 151>, //CLK_TOP_APLL2
<&topckgen_clk 148>, //CLK_TOP_APLL1_D4
<&topckgen_clk 153>, //CLK_TOP_APLL2_D4
<&topckgen_clk 93>, //CLK_TOP_APLL12_CK_DIV_I2SIN0
<&topckgen_clk 94>, //CLK_TOP_APLL12_CK_DIV_I2SIN1
<&topckgen_clk 95>, //CLK_TOP_APLL12_CK_DIV_I2SOUT0
<&topckgen_clk 96>, //CLK_TOP_APLL12_CK_DIV_I2SOUT1
<&topckgen_clk 97>, //CLK_TOP_APLL12_CK_DIV_FMI2S
<&topckgen_clk 98>, //CLK_TOP_APLL12_CK_DIV_TDMOUT_M
<&topckgen_clk 99>, //CLK_TOP_APLL12_CK_DIV_TDMOUT_B
<&topckgen_clk 44>, //CLK_TOP_AUD_1_SEL
<&topckgen_clk 45>, //CLK_TOP_AUD_2_SEL
<&topckgen_clk 78>, //CLK_TOP_APLL_I2SIN0_MCK_SEL
<&topckgen_clk 79>, //CLK_TOP_APLL_I2SIN1_MCK_SEL
<&topckgen_clk 84>, //CLK_TOP_APLL_I2SOUT0_MCK_SEL
<&topckgen_clk 85>, //CLK_TOP_APLL_I2SOUT1_MCK_SEL
<&topckgen_clk 90>, //CLK_TOP_APLL_FMI2S_MCK_SEL
<&topckgen_clk 91>, //CLK_TOP_APLL_TDMOUT_MCK_SEL
<&topckgen_clk 191>, //CLK_TOP_TCK_26M_MX9
<&pericfg_ao_clk 77>, //CLK_PERAO_AUDIO0
<&pericfg_ao_clk 78>, //CLK_PERAO_AUDIO1
<&pericfg_ao_clk 79>; //CLK_PERAO_AUDIO2
clock-names = "top_aud_intbus",
"top_aud_eng1",
"top_aud_eng2",
"top_aud_h",
"apll1",
"apll2",
"apll1_d4",
"apll2_d4",
"apll12_div_i2sin0",
"apll12_div_i2sin1",
"apll12_div_i2sout0",
"apll12_div_i2sout1",
"apll12_div_fmi2s",
"apll12_div_tdmout_m",
"apll12_div_tdmout_b",
"top_apll1",
"top_apll2",
"top_i2sin0",
"top_i2sin1",
"top_i2sout0",
"top_i2sout1",
"top_fmi2s",
"top_dptx",
"clk26m",
"aud_slv_ck_peri",
"aud_mst_ck_peri",
"aud_intbus_ck_peri";
};
};
...

View File

@ -0,0 +1,101 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/mediatek,mt8189-nau8825.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek MT8189 ASoC sound card
maintainers:
- Darren Ye <darren.ye@mediatek.com>
- Cyril Chao <cyril.chao@mediatek.com>
allOf:
- $ref: sound-card-common.yaml#
properties:
compatible:
enum:
- mediatek,mt8189-nau8825
- mediatek,mt8189-rt5650
- mediatek,mt8189-rt5682s
- mediatek,mt8189-rt5682i
- mediatek,mt8189-es8326
mediatek,platform:
$ref: /schemas/types.yaml#/definitions/phandle
description: The phandle of MT8189 ASoC platform.
patternProperties:
"^dai-link-[0-9]+$":
type: object
description:
Container for dai-link level properties and CODEC sub-nodes.
properties:
link-name:
description:
This property corresponds to the name of the BE dai-link to which
we are going to update parameters in this node.
enum:
- TDM_DPTX_BE
- I2SOUT0_BE
- I2SIN0_BE
- I2SOUT1_BE
codec:
description: Holds subnode which indicates codec dai.
type: object
additionalProperties: false
properties:
sound-dai:
minItems: 1
maxItems: 2
required:
- sound-dai
dai-format:
description: audio format.
enum:
- i2s
- right_j
- left_j
- dsp_a
- dsp_b
mediatek,clk-provider:
$ref: /schemas/types.yaml#/definitions/string
description: Indicates dai-link clock master.
enum:
- cpu
- codec
additionalProperties: false
required:
- link-name
required:
- compatible
- mediatek,platform
unevaluatedProperties: false
examples:
- |
sound {
compatible = "mediatek,mt8189-nau8825";
model = "mt8189_rt9123_8825";
mediatek,platform = <&afe>;
dai-link-0 {
link-name = "I2SOUT1_BE";
dai-format = "i2s";
mediatek,clk-provider = "cpu";
codec {
sound-dai = <&nau8825>;
};
};
};
...

View File

@ -1,40 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nuvoton,nau8540.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton Technology Corporation NAU85L40 Audio CODEC
maintainers:
- John Hsu <KCHSU0@nuvoton.com>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: nuvoton,nau8540
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1c {
compatible = "nuvoton,nau8540";
reg = <0x1c>;
};
};

View File

@ -1,45 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nuvoton,nau8810.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NAU8810/NAU8812/NAU8814 audio CODEC
maintainers:
- David Lin <CTLIN0@nuvoton.com>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- nuvoton,nau8810
- nuvoton,nau8812
- nuvoton,nau8814
reg:
maxItems: 1
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
#sound-dai-cells = <0>;
compatible = "nuvoton,nau8810";
reg = <0x1a>;
};
};

View File

@ -67,46 +67,72 @@ properties:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
if:
properties:
compatible:
contains:
const: nvidia,tegra210-admaif
allOf:
- if:
properties:
compatible:
contains:
const: nvidia,tegra210-admaif
then:
properties:
dmas:
description:
DMA channel specifiers, equally divided for Tx and Rx.
minItems: 1
maxItems: 20
dma-names:
items:
pattern: "^[rt]x(10|[1-9])$"
description:
Should be "rx1", "rx2" ... "rx10" for DMA Rx channel
Should be "tx1", "tx2" ... "tx10" for DMA Tx channel
minItems: 1
maxItems: 20
interconnects: false
interconnect-names: false
iommus: false
then:
properties:
dmas:
description:
DMA channel specifiers, equally divided for Tx and Rx.
minItems: 1
maxItems: 20
dma-names:
items:
pattern: "^[rt]x(10|[1-9])$"
description:
Should be "rx1", "rx2" ... "rx10" for DMA Rx channel
Should be "tx1", "tx2" ... "tx10" for DMA Tx channel
minItems: 1
maxItems: 20
interconnects: false
interconnect-names: false
iommus: false
- if:
properties:
compatible:
contains:
const: nvidia,tegra186-admaif
then:
properties:
dmas:
description:
DMA channel specifiers, equally divided for Tx and Rx.
minItems: 1
maxItems: 40
dma-names:
items:
pattern: "^[rt]x(1[0-9]|[1-9]|20)$"
description:
Should be "rx1", "rx2" ... "rx20" for DMA Rx channel
Should be "tx1", "tx2" ... "tx20" for DMA Tx channel
minItems: 1
maxItems: 40
else:
properties:
dmas:
description:
DMA channel specifiers, equally divided for Tx and Rx.
minItems: 1
maxItems: 40
dma-names:
items:
pattern: "^[rt]x(1[0-9]|[1-9]|20)$"
description:
Should be "rx1", "rx2" ... "rx20" for DMA Rx channel
Should be "tx1", "tx2" ... "tx20" for DMA Tx channel
minItems: 1
maxItems: 40
- if:
properties:
compatible:
contains:
const: nvidia,tegra264-admaif
then:
properties:
dmas:
description:
DMA channel specifiers, equally divided for Tx and Rx.
minItems: 1
maxItems: 64
dma-names:
items:
pattern: "^[rt]x(3[0-2]|[1-2][0-9]|[1-9])$"
description:
Should be "rx1", "rx2" ... "rx32" for DMA Rx channel
Should be "tx1", "tx2" ... "tx32" for DMA Tx channel
minItems: 1
maxItems: 64
required:
- compatible

View File

@ -1,44 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nxp,tfa9879.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP TFA9879 class-D audio amplifier
maintainers:
- Peter Rosin <peda@axentia.se>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: nxp,tfa9879
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
- '#sound-dai-cells'
unevaluatedProperties: false
examples:
- |
i2c1 {
#address-cells = <1>;
#size-cells = <0>;
amplifier@6c {
compatible = "nxp,tfa9879";
reg = <0x6c>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
#sound-dai-cells = <0>;
};
};

View File

@ -1,42 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/nxp,uda1342.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP uda1342 audio CODECs
maintainers:
- Binbin Zhou <zhoubinbin@loongson.cn>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: nxp,uda1342
reg:
maxItems: 1
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
- '#sound-dai-cells'
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "nxp,uda1342";
reg = <0x1a>;
#sound-dai-cells = <0>;
};
};

View File

@ -1,22 +0,0 @@
Texas Instruments pcm1789 DT bindings
PCM1789 is a simple audio codec that can be connected via
I2C or SPI. Currently, only I2C bus is supported.
Required properties:
- compatible: "ti,pcm1789"
Required properties on I2C:
- reg: the I2C address
- reset-gpios: GPIO to control the RESET pin
Examples:
audio-codec@4c {
compatible = "ti,pcm1789";
reg = <0x4c>;
reset-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>;
#sound-dai-cells = <0>;
};

View File

@ -1,27 +0,0 @@
Texas Instruments pcm179x DT bindings
This driver supports both the I2C and SPI bus.
Required properties:
- compatible: "ti,pcm1792a"
For required properties on SPI, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt
Required properties on I2C:
- reg: the I2C address
Examples:
codec_spi: 1792a@0 {
compatible = "ti,pcm1792a";
spi-max-frequency = <600000>;
};
codec_i2c: 1792a@4c {
compatible = "ti,pcm1792a";
reg = <0x4c>;
};

View File

@ -1,42 +0,0 @@
Texas Instruments PCM186x Universal Audio ADC
These devices support both I2C and SPI (configured with pin strapping
on the board).
Required properties:
- compatible : "ti,pcm1862",
"ti,pcm1863",
"ti,pcm1864",
"ti,pcm1865"
- reg : The I2C address of the device for I2C, the chip select
number for SPI.
- avdd-supply: Analog core power supply (3.3v)
- dvdd-supply: Digital core power supply
- iovdd-supply: Digital IO power supply
See regulator/regulator.txt for more information
CODEC input pins:
* VINL1
* VINR1
* VINL2
* VINR2
* VINL3
* VINR3
* VINL4
* VINR4
The pins can be used in referring sound node's audio-routing property.
Example:
pcm186x: audio-codec@4a {
compatible = "ti,pcm1865";
reg = <0x4a>;
avdd-supply = <&reg_3v3_analog>;
dvdd-supply = <&reg_3v3>;
iovdd-supply = <&reg_1v8>;
};

View File

@ -1,13 +0,0 @@
PCM5102a audio CODECs
These devices does not use I2C or SPI.
Required properties:
- compatible : set as "ti,pcm5102a"
Examples:
pcm5102a: pcm5102a {
compatible = "ti,pcm5102a";
};

View File

@ -14,12 +14,14 @@ properties:
oneOf:
- enum:
- qcom,sc7280-lpass-rx-macro
- qcom,sm6115-lpass-rx-macro
- qcom,sm8250-lpass-rx-macro
- qcom,sm8450-lpass-rx-macro
- qcom,sm8550-lpass-rx-macro
- qcom,sc8280xp-lpass-rx-macro
- items:
- enum:
- qcom,kaanapali-lpass-rx-macro
- qcom,sm8650-lpass-rx-macro
- qcom,sm8750-lpass-rx-macro
- qcom,x1e80100-lpass-rx-macro
@ -80,6 +82,23 @@ allOf:
- const: npl
- const: fsgen
- if:
properties:
compatible:
enum:
- qcom,sm6115-lpass-rx-macro
then:
properties:
clocks:
minItems: 4
maxItems: 4
clock-names:
items:
- const: mclk
- const: npl
- const: dcodec
- const: fsgen
- if:
properties:
compatible:

View File

@ -21,6 +21,7 @@ properties:
- qcom,sc8280xp-lpass-tx-macro
- items:
- enum:
- qcom,kaanapali-lpass-tx-macro
- qcom,sm8650-lpass-tx-macro
- qcom,sm8750-lpass-tx-macro
- qcom,x1e80100-lpass-tx-macro

View File

@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- qcom,sc7280-lpass-va-macro
- qcom,sm6115-lpass-va-macro
- qcom,sm8250-lpass-va-macro
- qcom,sm8450-lpass-va-macro
- qcom,sm8550-lpass-va-macro
@ -21,6 +22,7 @@ properties:
- items:
- enum:
- qcom,glymur-lpass-va-macro
- qcom,kaanapali-lpass-va-macro
- qcom,sm8650-lpass-va-macro
- qcom,sm8750-lpass-va-macro
- qcom,x1e80100-lpass-va-macro
@ -41,11 +43,7 @@ properties:
clock-names:
minItems: 1
items:
- const: mclk
- const: macro
- const: dcodec
- const: npl
maxItems: 4
clock-output-names:
maxItems: 1
@ -90,16 +88,33 @@ allOf:
clocks:
maxItems: 1
clock-names:
maxItems: 1
items:
- const: mclk
else:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
minItems: 3
maxItems: 3
items:
- const: mclk
- const: macro
- const: dcodec
- if:
properties:
compatible:
contains:
const: qcom,sm6115-lpass-va-macro
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
- const: mclk
- const: dcodec
- const: npl
- if:
properties:
compatible:
@ -111,8 +126,10 @@ allOf:
minItems: 3
maxItems: 3
clock-names:
minItems: 3
maxItems: 3
items:
- const: mclk
- const: macro
- const: dcodec
- if:
properties:
@ -127,8 +144,11 @@ allOf:
minItems: 4
maxItems: 4
clock-names:
minItems: 4
maxItems: 4
items:
- const: mclk
- const: macro
- const: dcodec
- const: npl
- if:
properties:
@ -142,8 +162,10 @@ allOf:
minItems: 3
maxItems: 3
clock-names:
minItems: 3
maxItems: 3
items:
- const: mclk
- const: macro
- const: dcodec
unevaluatedProperties: false

View File

@ -21,6 +21,7 @@ properties:
- items:
- enum:
- qcom,glymur-lpass-wsa-macro
- qcom,kaanapali-lpass-wsa-macro
- qcom,sm8650-lpass-wsa-macro
- qcom,sm8750-lpass-wsa-macro
- qcom,x1e80100-lpass-wsa-macro

View File

@ -23,6 +23,7 @@ properties:
- const: qcom,sdm845-sndcard
- items:
- enum:
- qcom,kaanapali-sndcard
- qcom,sm8550-sndcard
- qcom,sm8650-sndcard
- qcom,sm8750-sndcard
@ -38,6 +39,7 @@ properties:
- qcom,qcs8275-sndcard
- qcom,qcs9075-sndcard
- qcom,qcs9100-sndcard
- qcom,qrb2210-sndcard
- qcom,qrb4210-rb2-sndcard
- qcom,qrb5165-rb5-sndcard
- qcom,sc7180-qdsp6-sndcard

View File

@ -132,7 +132,7 @@ properties:
$ref: /schemas/gpio/qcom,wcd934x-gpio.yaml#
patternProperties:
"^.*@[0-9a-f]+$":
"@[0-9a-f]+$":
type: object
additionalProperties: true
description: |

View File

@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/spacemit,k1-i2s.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: K1 I2S controller
description:
The I2S bus (Inter-IC sound bus) is a serial link for digital
audio data transfer between devices in the system.
maintainers:
- Troy Mitchell <troy.mitchell@linux.spacemit.com>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: spacemit,k1-i2s
reg:
maxItems: 1
clocks:
items:
- description: clock for I2S sysclk
- description: clock for I2S bclk
- description: clock for I2S bus
- description: clock for I2S controller
clock-names:
items:
- const: sysclk
- const: bclk
- const: bus
- const: func
dmas:
minItems: 1
maxItems: 2
dma-names:
minItems: 1
items:
- const: tx
- const: rx
resets:
maxItems: 1
port:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- dmas
- dma-names
- resets
- "#sound-dai-cells"
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/spacemit,k1-syscon.h>
i2s@d4026000 {
compatible = "spacemit,k1-i2s";
reg = <0xd4026000 0x30>;
clocks = <&syscon_mpmu CLK_I2S_SYSCLK>,
<&syscon_mpmu CLK_I2S_BCLK>,
<&syscon_apbc CLK_SSPA0_BUS>,
<&syscon_apbc CLK_SSPA0>;
clock-names = "sysclk", "bclk", "bus", "func";
dmas = <&pdma0 21>, <&pdma0 22>;
dma-names = "tx", "rx";
resets = <&syscon_apbc RESET_SSPA0>;
#sound-dai-cells = <0>;
};

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/ti,pcm1862.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments PCM186x Universal Audio ADC
maintainers:
- Ranganath V N <vnranganath.20@gmail.com>
description: |
The Texas Instruments PCM186x family are multi-channel audio ADCs
that support both I2C and SPI control interfaces, selected by
pin strapping. These devices include on-chip programmable gain
amplifiers and support differential or single-ended analog inputs.
CODEC input pins:
* VINL1
* VINR1
* VINL2
* VINR2
* VINL3
* VINR3
* VINL4
* VINR4
The pins can be used in referring sound node's audio-routing property.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- ti,pcm1862
- ti,pcm1863
- ti,pcm1864
- ti,pcm1865
reg:
maxItems: 1
avdd-supply: true
dvdd-supply: true
iovdd-supply: true
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
- avdd-supply
- dvdd-supply
- iovdd-supply
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@4a {
compatible = "ti,pcm1865";
reg = <0x4a>;
avdd-supply = <&reg_3v3_analog>;
dvdd-supply = <&reg_3v3>;
iovdd-supply = <&reg_1v8>;
};
};

View File

@ -24,21 +24,26 @@ description: |
Instruments Smart Amp speaker protection algorithm. The
integrated speaker voltage and current sense provides for real time
monitoring of loudspeaker behavior.
The TAS5802/TAS5815/TAS5825/TAS5827/TAS5828 is a stereo, digital input
Class-D audio amplifier optimized for efficiently driving high peak
power into small loudspeakers. An integrated on-chip DSP supports
Texas Instruments Smart Amp speaker protection algorithm.
The TAS5802/TAS5815/TAS5822/TAS5825/TAS5827/TAS5828 is a stereo,
digital input Class-D audio amplifier optimized for efficiently driving
high peak power into small loudspeakers. An integrated on-chip DSP
supports Texas Instruments Smart Amp speaker protection algorithm.
Specifications about the audio amplifier can be found at:
https://www.ti.com/lit/gpn/tas2120
https://www.ti.com/lit/gpn/tas2320
https://www.ti.com/lit/gpn/tas2563
https://www.ti.com/lit/gpn/tas2572
https://www.ti.com/lit/gpn/tas2574
https://www.ti.com/lit/gpn/tas2781
https://www.ti.com/lit/gpn/tas5806m
https://www.ti.com/lit/gpn/tas5806md
https://www.ti.com/lit/gpn/tas5815
https://www.ti.com/lit/gpn/tas5822m
https://www.ti.com/lit/gpn/tas5825m
https://www.ti.com/lit/gpn/tas5827
https://www.ti.com/lit/gpn/tas5828m
https://www.ti.com/lit/gpn/tas5830
properties:
compatible:
@ -57,12 +62,18 @@ properties:
ti,tas2563: 6.1-W Boosted Class-D Audio Amplifier With Integrated
DSP and IV Sense, 16/20/24/32bit stereo I2S or multichannel TDM.
ti,tas2568: 5.3-W Digital Input Smart Amp with I/V Sense and Integrated
10.75-V Class-H Boost
ti,tas2570: 5.8-W Digital Input smart amp with I/V sense and integrated
11-V Class-H Boost
ti,tas2572: 6.6-W Digital Input smart amp with I/V sense and integrated
13-V Class-H Boost
ti,tas2574: 8.5-W Digital Input smart amp with I/V sense and integrated
15-V Class-H Boost
ti,tas2781: 24-V Class-D Amplifier with Real Time Integrated Speaker
Protection and Audio Processing, 16/20/24/32bit stereo I2S or
multichannel TDM.
@ -71,9 +82,20 @@ properties:
Audio Amplifier with 96-Khz Extended Processing and Low Idle Power
Dissipation.
ti,tas5806m: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
Class-D Audio Amplifier with Enhanced Processing and Low Power
Dissipation.
ti,tas5806md: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
Class-D Audio Amplifier with Enhanced Processing and DirectPath(TM)
HP Driver
ti,tas5815: 30-W, Digital Input, Stereo, Closed-loop Class-D Audio
Amplifier with 96 kHz Enhanced Processing
ti,tas5822: 35-W, Digital Input, Stereo, Closed-Loop Class-D Audio
Amplifier with 96 kHz Enhanced Processing
ti,tas5825: 38-W Stereo, Inductor-Less, Digital Input, Closed-Loop 4.5V
to 26.4V Class-D Audio Amplifier with 192-kHz Extended Audio Processing.
@ -82,6 +104,9 @@ properties:
ti,tas5828: 50-W Stereo, Digital Input, High Efficiency Closed-Loop
Class-D Amplifier with Hybrid-Pro Algorithm
ti,tas5830: 65-W Stereo, Digital Input, High Efficiency Closed-Loop
Class-D Amplifier with Class-H Algorithm
oneOf:
- items:
- enum:
@ -90,13 +115,19 @@ properties:
- ti,tas2120
- ti,tas2320
- ti,tas2563
- ti,tas2568
- ti,tas2570
- ti,tas2572
- ti,tas2574
- ti,tas5802
- ti,tas5806m
- ti,tas5806md
- ti,tas5815
- ti,tas5822
- ti,tas5825
- ti,tas5827
- ti,tas5828
- ti,tas5830
- const: ti,tas2781
- enum:
- ti,tas2781
@ -132,6 +163,8 @@ allOf:
- ti,tas2118
- ti,tas2120
- ti,tas2320
- ti,tas2568
- ti,tas2574
then:
properties:
reg:
@ -207,6 +240,22 @@ allOf:
minimum: 0x54
maximum: 0x57
- if:
properties:
compatible:
contains:
enum:
- ti,tas5806m
- ti,tas5806md
- ti,tas5822
then:
properties:
reg:
maxItems: 4
items:
minimum: 0x2c
maximum: 0x2f
- if:
properties:
compatible:
@ -214,6 +263,7 @@ allOf:
enum:
- ti,tas5827
- ti,tas5828
- ti,tas5830
then:
properties:
reg:

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/trivial-codec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Trivial Audio Codec
maintainers:
- Rob Herring <robh@kernel.org>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
# Analog Devices SSM2602 I2S audio CODEC devices
- adi,ssm2602
- adi,ssm2603
- adi,ssm2604
- adi,ssm3515
# Cirrus Logic CS4265 audio DAC
- cirrus,cs4265
- cirrus,cs4341a
- cirrus,cs4349
- dlg,da9055-codec
# Nuvoton Technology Corporation NAU85L40 Audio CODEC
- nuvoton,nau8540
- nuvoton,nau8810
- nuvoton,nau8812
- nuvoton,nau8814
# NXP TFA9879 class-D audio amplifier
- nxp,tfa9879
- nxp,uda1342
- sdw3019f836300
- ti,pcm1789
- ti,pcm1792a
- ti,pcm5102a
- wlf,wm8510
- wlf,wm8523
- wlf,wm8580
- wlf,wm8581
- wlf,wm8711
- wlf,wm8728
- wlf,wm8737
- wlf,wm8750
- wlf,wm8753
- wlf,wm8770
- wlf,wm8776
- wlf,wm8961
- wlf,wm8974
- wlf,wm8987
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
reset-gpios:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8523";
reg = <0x1a>;
};
};

View File

@ -1,41 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8510.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8510 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8510
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8510";
reg = <0x1a>;
};
};

View File

@ -1,40 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8523.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8523 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8523
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8523";
reg = <0x1a>;
};
};

View File

@ -1,42 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8580.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8580 and WM8581 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- wlf,wm8580
- wlf,wm8581
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8580";
reg = <0x1a>;
};
};

View File

@ -1,40 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8711.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8711 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8711
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8711";
reg = <0x1a>;
};
};

View File

@ -1,40 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8728.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8728 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8728
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8728";
reg = <0x1a>;
};
};

View File

@ -1,40 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8737.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8737 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8737
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8737";
reg = <0x1a>;
};
};

View File

@ -1,42 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8750.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8750 and WM8987 audio CODECs
description: |
These devices support both I2C and SPI (configured with pin strapping
on the board).
maintainers:
- Mark Brown <broonie@kernel.org>
properties:
compatible:
enum:
- wlf,wm8750
- wlf,wm8987
reg:
description:
The I2C address of the device for I2C, the chip select number for SPI
maxItems: 1
additionalProperties: false
required:
- reg
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8750";
reg = <0x1a>;
};
};

View File

@ -1,62 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8753.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8753 audio CODEC
description: |
Pins on the device (for linking into audio routes):
* LOUT1
* LOUT2
* ROUT1
* ROUT2
* MONO1
* MONO2
* OUT3
* OUT4
* LINE1
* LINE2
* RXP
* RXN
* ACIN
* ACOP
* MIC1N
* MIC1
* MIC2N
* MIC2
* Mic Bias
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8753
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8753";
reg = <0x1a>;
};
};

View File

@ -1,41 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8776.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8776 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8776
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8776";
reg = <0x1a>;
};
};

View File

@ -1,43 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8961.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Wolfson WM8961 Ultra-Low Power Stereo CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8961
reg:
maxItems: 1
'#sound-dai-cells':
const: 0
required:
- compatible
- reg
- '#sound-dai-cells'
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
wm8961: codec@4a {
compatible = "wlf,wm8961";
reg = <0x4a>;
#sound-dai-cells = <0>;
};
};

View File

@ -1,41 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/wlf,wm8974.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: WM8974 audio CODEC
maintainers:
- patches@opensource.cirrus.com
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: wlf,wm8974
reg:
maxItems: 1
"#sound-dai-cells":
const: 0
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "wlf,wm8974";
reg = <0x1a>;
};
};

View File

@ -1,16 +0,0 @@
WM8770 audio CODEC
This device supports SPI.
Required properties:
- compatible : "wlf,wm8770"
- reg : the chip select number.
Example:
wm8770: codec@1 {
compatible = "wlf,wm8770";
reg = <1>;
};

View File

@ -1758,6 +1758,7 @@ S: Supported
W: http://wiki.analog.com/
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/sound/adi,*
F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/codecs/ad1*
F: sound/soc/codecs/ad7*
F: sound/soc/codecs/adau*
@ -2421,9 +2422,9 @@ M: Martin Povišer <povik+lin@cutebit.org>
L: asahi@lists.linux.dev
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
F: Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
F: Documentation/devicetree/bindings/sound/apple,*
F: Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/apple/*
F: sound/soc/codecs/cs42l83-i2c.c
F: sound/soc/codecs/cs42l84.*
@ -18749,7 +18750,7 @@ NXP TFA9879 DRIVER
M: Peter Rosin <peda@axentia.se>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml
F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/codecs/tfa9879*
NXP-NCI NFC DRIVER
@ -27799,6 +27800,7 @@ F: Documentation/devicetree/bindings/extcon/wlf,arizona.yaml
F: Documentation/devicetree/bindings/mfd/wlf,arizona.yaml
F: Documentation/devicetree/bindings/mfd/wm831x.txt
F: Documentation/devicetree/bindings/regulator/wlf,arizona.yaml
F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: Documentation/devicetree/bindings/sound/wlf,*.yaml
F: Documentation/devicetree/bindings/sound/wm*
F: Documentation/hwmon/wm83??.rst

View File

@ -15,6 +15,7 @@
struct regmap_mbq_context {
struct device *dev;
struct sdw_slave *sdw;
struct regmap_sdw_mbq_cfg cfg;
@ -46,7 +47,7 @@ static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned i
static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
struct regmap_mbq_context *ctx)
{
struct device *dev = &slave->dev;
struct device *dev = ctx->dev;
int val, ret = 0;
dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
@ -96,8 +97,7 @@ static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_mbq_context *ctx = context;
struct device *dev = ctx->dev;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct sdw_slave *slave = ctx->sdw;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
@ -156,8 +156,7 @@ static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_mbq_context *ctx = context;
struct device *dev = ctx->dev;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct sdw_slave *slave = ctx->sdw;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
@ -208,6 +207,7 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config)
{
@ -218,6 +218,7 @@ regmap_sdw_mbq_gen_context(struct device *dev,
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
ctx->sdw = sdw;
if (mbq_config)
ctx->cfg = *mbq_config;
@ -228,7 +229,7 @@ regmap_sdw_mbq_gen_context(struct device *dev,
return ctx;
}
struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@ -241,16 +242,16 @@ struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
if (ret)
return ERR_PTR(ret);
ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
return __regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@ -263,11 +264,11 @@ struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
if (ret)
return ERR_PTR(ret);
ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
return __devm_regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);

View File

@ -9,9 +9,11 @@
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/cleanup.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@ -316,44 +318,6 @@ struct cs_dsp_alg_region_list_item {
struct cs_dsp_alg_region alg_region;
};
struct cs_dsp_buf {
struct list_head list;
void *buf;
};
static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len,
struct list_head *list)
{
struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (buf == NULL)
return NULL;
buf->buf = vmalloc(len);
if (!buf->buf) {
kfree(buf);
return NULL;
}
memcpy(buf->buf, src, len);
if (list)
list_add_tail(&buf->list, list);
return buf;
}
static void cs_dsp_buf_free(struct list_head *list)
{
while (!list_empty(list)) {
struct cs_dsp_buf *buf = list_first_entry(list,
struct cs_dsp_buf,
list);
list_del(&buf->list);
vfree(buf->buf);
kfree(buf);
}
}
/**
* cs_dsp_mem_region_name() - Return a name string for a memory type
* @type: the memory type to match
@ -388,18 +352,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, "FW_CS_DSP");
#ifdef CONFIG_DEBUG_FS
static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
kfree(dsp->wmfw_file_name);
dsp->wmfw_file_name = tmp;
dsp->wmfw_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s)
{
char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
kfree(dsp->bin_file_name);
dsp->bin_file_name = tmp;
dsp->bin_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
@ -410,24 +370,33 @@ static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
dsp->bin_file_name = NULL;
}
static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp,
char __user *user_buf,
size_t count, loff_t *ppos,
const char **pstr)
{
const char *str __free(kfree) = NULL;
scoped_guard(mutex, &dsp->pwr_lock) {
if (!*pstr)
return 0;
str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
if (!str)
return -ENOMEM;
return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
}
}
static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
ssize_t ret;
mutex_lock(&dsp->pwr_lock);
if (!dsp->wmfw_file_name || !dsp->booted)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
dsp->wmfw_file_name,
strlen(dsp->wmfw_file_name));
mutex_unlock(&dsp->pwr_lock);
return ret;
return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
&dsp->wmfw_file_name);
}
static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
@ -435,19 +404,9 @@ static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
ssize_t ret;
mutex_lock(&dsp->pwr_lock);
if (!dsp->bin_file_name || !dsp->booted)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
dsp->bin_file_name,
strlen(dsp->bin_file_name));
mutex_unlock(&dsp->pwr_lock);
return ret;
return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
&dsp->bin_file_name);
}
static const struct {
@ -479,9 +438,11 @@ static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored)
struct cs_dsp_coeff_ctl *ctl;
unsigned int reg;
guard(mutex)(&dsp->pwr_lock);
list_for_each_entry(ctl, &dsp->ctl_list, list) {
cs_dsp_coeff_base_reg(ctl, &reg, 0);
seq_printf(s, "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",
seq_printf(s, "%22.*s: %#8x %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",
ctl->subname_len, ctl->subname, ctl->len,
cs_dsp_mem_region_name(ctl->alg_region.type),
ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type,
@ -1028,7 +989,7 @@ static void cs_dsp_signal_event_controls(struct cs_dsp *dsp,
static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl)
{
kfree(ctl->cache);
kvfree(ctl->cache);
kfree(ctl->subname);
kfree(ctl);
}
@ -1078,7 +1039,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
ctl->type = type;
ctl->offset = offset;
ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
ctl->cache = kvzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_subname;
@ -1096,7 +1057,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
err_list_del:
list_del(&ctl->list);
kfree(ctl->cache);
kvfree(ctl->cache);
err_ctl_subname:
kfree(ctl->subname);
err_ctl:
@ -1485,7 +1446,9 @@ 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;
struct cs_dsp_buf *buf;
u8 *buf __free(kfree) = NULL;
size_t buf_len = 0;
size_t region_len;
unsigned int reg;
int regions = 0;
int ret, offset, type;
@ -1605,23 +1568,23 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
region_name);
if (reg) {
buf = cs_dsp_buf_alloc(region->data,
le32_to_cpu(region->len),
&buf_list);
if (!buf) {
cs_dsp_err(dsp, "Out of memory\n");
ret = -ENOMEM;
goto out_fw;
region_len = le32_to_cpu(region->len);
if (region_len > buf_len) {
buf_len = round_up(region_len, PAGE_SIZE);
kfree(buf);
buf = kmalloc(buf_len, GFP_KERNEL | GFP_DMA);
if (!buf) {
ret = -ENOMEM;
goto out_fw;
}
}
ret = regmap_raw_write(regmap, reg, buf->buf,
le32_to_cpu(region->len));
memcpy(buf, region->data, region_len);
ret = regmap_raw_write(regmap, reg, buf, region_len);
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
file, regions,
le32_to_cpu(region->len), offset,
region_name, ret);
"%s.%d: Failed to write %zu bytes at %d in %s: %d\n",
file, regions, region_len, offset, region_name, ret);
goto out_fw;
}
}
@ -1638,8 +1601,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = 0;
out_fw:
cs_dsp_buf_free(&buf_list);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@ -2171,7 +2132,9 @@ 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;
struct cs_dsp_buf *buf;
u8 *buf __free(kfree) = NULL;
size_t buf_len = 0;
size_t region_len;
if (!firmware)
return 0;
@ -2313,20 +2276,22 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
}
if (reg) {
buf = cs_dsp_buf_alloc(blk->data,
le32_to_cpu(blk->len),
&buf_list);
if (!buf) {
cs_dsp_err(dsp, "Out of memory\n");
ret = -ENOMEM;
goto out_fw;
region_len = le32_to_cpu(blk->len);
if (region_len > buf_len) {
buf_len = round_up(region_len, PAGE_SIZE);
kfree(buf);
buf = kmalloc(buf_len, GFP_KERNEL | GFP_DMA);
if (!buf) {
ret = -ENOMEM;
goto out_fw;
}
}
cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
file, blocks, le32_to_cpu(blk->len),
reg);
ret = regmap_raw_write(regmap, reg, buf->buf,
le32_to_cpu(blk->len));
memcpy(buf, blk->data, region_len);
cs_dsp_dbg(dsp, "%s.%d: Writing %zu bytes at %x\n",
file, blocks, region_len, reg);
ret = regmap_raw_write(regmap, reg, buf, region_len);
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write to %x in %s: %d\n",
@ -2346,8 +2311,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
ret = 0;
out_fw:
cs_dsp_buf_free(&buf_list);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@ -2366,6 +2329,9 @@ static int cs_dsp_create_name(struct cs_dsp *dsp)
return 0;
}
static const struct cs_dsp_client_ops cs_dsp_default_client_ops = {
};
static int cs_dsp_common_init(struct cs_dsp *dsp)
{
int ret;
@ -2379,6 +2345,9 @@ static int cs_dsp_common_init(struct cs_dsp *dsp)
mutex_init(&dsp->pwr_lock);
if (!dsp->client_ops)
dsp->client_ops = &cs_dsp_default_client_ops;
#ifdef CONFIG_DEBUG_FS
/* Ensure this is invalid if client never provides a debugfs root */
dsp->debugfs_root = ERR_PTR(-ENODEV);

View File

@ -600,6 +600,7 @@ KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
{ .ops = &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
{ .ops = NULL, .case_name = "NULL ops" },
};
KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,

View File

@ -6,6 +6,9 @@
config GPIOLIB_LEGACY
def_bool y
config HAVE_SHARED_GPIOS
bool
menuconfig GPIOLIB
bool "GPIO Support"
help
@ -50,6 +53,11 @@ config OF_GPIO_MM_GPIOCHIP
this symbol, but new drivers should use the generic gpio-regmap
infrastructure instead.
config GPIO_SHARED
def_bool y
depends on HAVE_SHARED_GPIOS || COMPILE_TEST
select AUXILIARY_BUS
config DEBUG_GPIO
bool "Debug GPIO calls"
depends on DEBUG_KERNEL
@ -2017,6 +2025,15 @@ config GPIO_SIM
This enables the GPIO simulator - a configfs-based GPIO testing
driver.
config GPIO_SHARED_PROXY
tristate "Proxy driver for non-exclusive GPIOs"
default m
depends on GPIO_SHARED || COMPILE_TEST
select AUXILIARY_BUS
help
This enables the GPIO shared proxy driver - an abstraction layer
for GPIO pins that are shared by multiple devices.
endmenu
menu "GPIO Debugging utilities"

View File

@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
@ -159,6 +160,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SHARED_PROXY) += gpio-shared-proxy.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o

View File

@ -0,0 +1,333 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 Linaro Ltd.
*/
#include <linux/auxiliary_bus.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/string_choices.h>
#include <linux/types.h>
#include "gpiolib-shared.h"
struct gpio_shared_proxy_data {
struct gpio_chip gc;
struct gpio_shared_desc *shared_desc;
struct device *dev;
bool voted_high;
};
static int
gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,
int (*set_func)(struct gpio_desc *desc, int value),
int value)
{
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
struct gpio_desc *desc = shared_desc->desc;
int ret = 0;
gpio_shared_lockdep_assert(shared_desc);
if (value) {
/* User wants to set value to high. */
if (proxy->voted_high)
/* Already voted for high, nothing to do. */
goto out;
/* Haven't voted for high yet. */
if (!shared_desc->highcnt) {
/*
* Current value is low, need to actually set value
* to high.
*/
ret = set_func(desc, 1);
if (ret)
goto out;
}
shared_desc->highcnt++;
proxy->voted_high = true;
goto out;
}
/* Desired value is low. */
if (!proxy->voted_high)
/* We didn't vote for high, nothing to do. */
goto out;
/* We previously voted for high. */
if (shared_desc->highcnt == 1) {
/* This is the last remaining vote for high, set value to low. */
ret = set_func(desc, 0);
if (ret)
goto out;
}
shared_desc->highcnt--;
proxy->voted_high = false;
out:
if (shared_desc->highcnt)
dev_dbg(proxy->dev,
"Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
str_high_low(value), shared_desc->highcnt);
else
dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
return ret;
}
static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
guard(gpio_shared_desc_lock)(shared_desc);
proxy->shared_desc->usecnt++;
dev_dbg(proxy->dev, "Shared GPIO requested, number of users: %u\n",
proxy->shared_desc->usecnt);
return 0;
}
static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
guard(gpio_shared_desc_lock)(shared_desc);
proxy->shared_desc->usecnt--;
dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n",
proxy->shared_desc->usecnt);
}
static int gpio_shared_proxy_set_config(struct gpio_chip *gc,
unsigned int offset, unsigned long cfg)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
struct gpio_desc *desc = shared_desc->desc;
int ret;
guard(gpio_shared_desc_lock)(shared_desc);
if (shared_desc->usecnt > 1) {
if (shared_desc->cfg != cfg) {
dev_dbg(proxy->dev,
"Shared GPIO's configuration already set, accepting changes but users may conflict!!\n");
} else {
dev_dbg(proxy->dev, "Equal config requested, nothing to do\n");
return 0;
}
}
ret = gpiod_set_config(desc, cfg);
if (ret && ret != -ENOTSUPP)
return ret;
shared_desc->cfg = cfg;
return 0;
}
static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
struct gpio_desc *desc = shared_desc->desc;
int dir;
guard(gpio_shared_desc_lock)(shared_desc);
if (shared_desc->usecnt == 1) {
dev_dbg(proxy->dev,
"Only one user of this shared GPIO, allowing to set direction to input\n");
return gpiod_direction_input(desc);
}
dir = gpiod_get_direction(desc);
if (dir < 0)
return dir;
if (dir == GPIO_LINE_DIRECTION_OUT) {
dev_dbg(proxy->dev,
"Shared GPIO's direction already set to output, refusing to change\n");
return -EPERM;
}
return 0;
}
static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
unsigned int offset, int value)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
struct gpio_shared_desc *shared_desc = proxy->shared_desc;
struct gpio_desc *desc = shared_desc->desc;
int ret, dir;
guard(gpio_shared_desc_lock)(shared_desc);
if (shared_desc->usecnt == 1) {
dev_dbg(proxy->dev,
"Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n",
str_high_low(value));
ret = gpiod_direction_output(desc, value);
if (ret)
return ret;
if (value) {
proxy->voted_high = true;
shared_desc->highcnt = 1;
} else {
proxy->voted_high = false;
shared_desc->highcnt = 0;
}
return 0;
}
dir = gpiod_get_direction(desc);
if (dir < 0)
return dir;
if (dir == GPIO_LINE_DIRECTION_IN) {
dev_dbg(proxy->dev,
"Shared GPIO's direction already set to input, refusing to change\n");
return -EPERM;
}
return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value);
}
static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpiod_get_value(proxy->shared_desc->desc);
}
static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpiod_get_value_cansleep(proxy->shared_desc->desc);
}
static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy,
int (*set_func)(struct gpio_desc *desc, int value),
int value)
{
guard(gpio_shared_desc_lock)(proxy->shared_desc);
return gpio_shared_proxy_set_unlocked(proxy, set_func, value);
}
static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value);
}
static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc,
unsigned int offset, int value)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value);
}
static int gpio_shared_proxy_get_direction(struct gpio_chip *gc,
unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpiod_get_direction(proxy->shared_desc->desc);
}
static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
return gpiod_to_irq(proxy->shared_desc->desc);
}
static int gpio_shared_proxy_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct gpio_shared_proxy_data *proxy;
struct gpio_shared_desc *shared_desc;
struct device *dev = &adev->dev;
struct gpio_chip *gc;
shared_desc = devm_gpiod_shared_get(dev);
if (IS_ERR(shared_desc))
return PTR_ERR(shared_desc);
proxy = devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL);
if (!proxy)
return -ENOMEM;
proxy->shared_desc = shared_desc;
proxy->dev = dev;
gc = &proxy->gc;
gc->base = -1;
gc->ngpio = 1;
gc->label = dev_name(dev);
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->can_sleep = shared_desc->can_sleep;
gc->request = gpio_shared_proxy_request;
gc->free = gpio_shared_proxy_free;
gc->set_config = gpio_shared_proxy_set_config;
gc->direction_input = gpio_shared_proxy_direction_input;
gc->direction_output = gpio_shared_proxy_direction_output;
if (gc->can_sleep) {
gc->set = gpio_shared_proxy_set_cansleep;
gc->get = gpio_shared_proxy_get_cansleep;
} else {
gc->set = gpio_shared_proxy_set;
gc->get = gpio_shared_proxy_get;
}
gc->get_direction = gpio_shared_proxy_get_direction;
gc->to_irq = gpio_shared_proxy_to_irq;
return devm_gpiochip_add_data(dev, &proxy->gc, proxy);
}
static const struct auxiliary_device_id gpio_shared_proxy_id_table[] = {
{ .name = "gpiolib_shared.proxy" },
{},
};
MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table);
static struct auxiliary_driver gpio_shared_proxy_driver = {
.driver = {
.name = "gpio-shared-proxy",
},
.probe = gpio_shared_proxy_probe,
.id_table = gpio_shared_proxy_id_table,
};
module_auxiliary_driver(gpio_shared_proxy_driver);
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_DESCRIPTION("Shared GPIO mux driver.");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,558 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 Linaro Ltd.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/auxiliary_bus.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/idr.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "gpiolib.h"
#include "gpiolib-shared.h"
/* Represents a single reference to a GPIO pin. */
struct gpio_shared_ref {
struct list_head list;
/* Firmware node associated with this GPIO's consumer. */
struct fwnode_handle *fwnode;
/* GPIO flags this consumer uses for the request. */
enum gpiod_flags flags;
char *con_id;
int dev_id;
struct auxiliary_device adev;
struct gpiod_lookup_table *lookup;
};
/* Represents a single GPIO pin. */
struct gpio_shared_entry {
struct list_head list;
/* Firmware node associated with the GPIO controller. */
struct fwnode_handle *fwnode;
/* Hardware offset of the GPIO within its chip. */
unsigned int offset;
/* Index in the property value array. */
size_t index;
struct gpio_shared_desc *shared_desc;
struct kref ref;
struct list_head refs;
};
static LIST_HEAD(gpio_shared_list);
static DEFINE_MUTEX(gpio_shared_lock);
static DEFINE_IDA(gpio_shared_ida);
static struct gpio_shared_entry *
gpio_shared_find_entry(struct fwnode_handle *controller_node,
unsigned int offset)
{
struct gpio_shared_entry *entry;
list_for_each_entry(entry, &gpio_shared_list, list) {
if (entry->fwnode == controller_node && entry->offset == offset)
return entry;
}
return NULL;
}
#if IS_ENABLED(CONFIG_OF)
static int gpio_shared_of_traverse(struct device_node *curr)
{
struct gpio_shared_entry *entry;
size_t con_id_len, suffix_len;
struct fwnode_handle *fwnode;
struct of_phandle_args args;
struct property *prop;
unsigned int offset;
const char *suffix;
int ret, count, i;
for_each_property_of_node(curr, prop) {
/*
* The standard name for a GPIO property is "foo-gpios"
* or "foo-gpio". Some bindings also use "gpios" or "gpio".
* There are some legacy device-trees which have a different
* naming convention and for which we have rename quirks in
* place in gpiolib-of.c. I don't think any of them require
* support for shared GPIOs so for now let's just ignore
* them. We can always just export the quirk list and
* iterate over it here.
*/
if (!strends(prop->name, "-gpios") &&
!strends(prop->name, "-gpio") &&
strcmp(prop->name, "gpios") != 0 &&
strcmp(prop->name, "gpio") != 0)
continue;
count = of_count_phandle_with_args(curr, prop->name,
"#gpio-cells");
if (count <= 0)
continue;
for (i = 0; i < count; i++) {
struct device_node *np __free(device_node) = NULL;
ret = of_parse_phandle_with_args(curr, prop->name,
"#gpio-cells", i,
&args);
if (ret)
continue;
np = args.np;
if (!of_property_present(np, "gpio-controller"))
continue;
/*
* We support 1, 2 and 3 cell GPIO bindings in the
* kernel currently. There's only one old MIPS dts that
* has a one-cell binding but there's no associated
* consumer so it may as well be an error. There don't
* seem to be any 3-cell users of non-exclusive GPIOs,
* so we can skip this as well. Let's occupy ourselves
* with the predominant 2-cell binding with the first
* cell indicating the hardware offset of the GPIO and
* the second defining the GPIO flags of the request.
*/
if (args.args_count != 2)
continue;
fwnode = of_fwnode_handle(args.np);
offset = args.args[0];
entry = gpio_shared_find_entry(fwnode, offset);
if (!entry) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->fwnode = fwnode_handle_get(fwnode);
entry->offset = offset;
entry->index = count;
INIT_LIST_HEAD(&entry->refs);
list_add_tail(&entry->list, &gpio_shared_list);
}
struct gpio_shared_ref *ref __free(kfree) =
kzalloc(sizeof(*ref), GFP_KERNEL);
if (!ref)
return -ENOMEM;
ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr));
ref->flags = args.args[1];
if (strends(prop->name, "gpios"))
suffix = "-gpios";
else if (strends(prop->name, "gpio"))
suffix = "-gpio";
else
suffix = NULL;
if (!suffix)
continue;
/* We only set con_id if there's actually one. */
if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) {
ref->con_id = kstrdup(prop->name, GFP_KERNEL);
if (!ref->con_id)
return -ENOMEM;
con_id_len = strlen(ref->con_id);
suffix_len = strlen(suffix);
ref->con_id[con_id_len - suffix_len] = '\0';
}
ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL);
if (ref->dev_id < 0) {
kfree(ref->con_id);
return -ENOMEM;
}
if (!list_empty(&entry->refs))
pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
entry->offset, fwnode_get_name(entry->fwnode));
list_add_tail(&no_free_ptr(ref)->list, &entry->refs);
}
}
for_each_child_of_node_scoped(curr, child) {
ret = gpio_shared_of_traverse(child);
if (ret)
return ret;
}
return 0;
}
static int gpio_shared_of_scan(void)
{
return gpio_shared_of_traverse(of_root);
}
#else
static int gpio_shared_of_scan(void)
{
return 0;
}
#endif /* CONFIG_OF */
static void gpio_shared_adev_release(struct device *dev)
{
}
static int gpio_shared_make_adev(struct gpio_device *gdev,
struct gpio_shared_ref *ref)
{
struct auxiliary_device *adev = &ref->adev;
int ret;
lockdep_assert_held(&gpio_shared_lock);
memset(adev, 0, sizeof(*adev));
adev->id = ref->dev_id;
adev->name = "proxy";
adev->dev.parent = gdev->dev.parent;
adev->dev.release = gpio_shared_adev_release;
ret = auxiliary_device_init(adev);
if (ret)
return ret;
ret = auxiliary_device_add(adev);
if (ret) {
auxiliary_device_uninit(adev);
return ret;
}
pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
dev_name(&adev->dev), gpio_device_get_label(gdev));
return 0;
}
int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
{
const char *dev_id = dev_name(consumer);
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
struct gpiod_lookup_table *lookup __free(kfree) =
kzalloc(struct_size(lookup, table, 2), GFP_KERNEL);
if (!lookup)
return -ENOMEM;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
if (!device_match_fwnode(consumer, ref->fwnode))
continue;
/* We've already done that on a previous request. */
if (ref->lookup)
return 0;
char *key __free(kfree) =
kasprintf(GFP_KERNEL,
KBUILD_MODNAME ".proxy.%u",
ref->adev.id);
if (!key)
return -ENOMEM;
pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
dev_id, key, ref->con_id ?: "none");
lookup->dev_id = dev_id;
lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
ref->con_id, lflags);
gpiod_add_lookup_table(no_free_ptr(lookup));
return 0;
}
}
/* We warn here because this can only happen if the programmer borked. */
WARN_ON(1);
return -ENOENT;
}
static void gpio_shared_remove_adev(struct auxiliary_device *adev)
{
lockdep_assert_held(&gpio_shared_lock);
auxiliary_device_uninit(adev);
auxiliary_device_delete(adev);
}
int gpio_device_setup_shared(struct gpio_device *gdev)
{
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
unsigned long *flags;
int ret;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
if (gdev->dev.parent == &ref->adev.dev) {
/*
* This is a shared GPIO proxy. Mark its
* descriptor as such and return here.
*/
__set_bit(GPIOD_FLAG_SHARED_PROXY,
&gdev->descs[0].flags);
return 0;
}
}
}
/*
* This is not a shared GPIO proxy but it still may be the device
* exposing shared pins. Find them and create the proxy devices.
*/
list_for_each_entry(entry, &gpio_shared_list, list) {
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
if (list_count_nodes(&entry->refs) <= 1)
continue;
flags = &gdev->descs[entry->offset].flags;
__set_bit(GPIOD_FLAG_SHARED, flags);
/*
* Shared GPIOs are not requested via the normal path. Make
* them inaccessible to anyone even before we register the
* chip.
*/
__set_bit(GPIOD_FLAG_REQUESTED, flags);
pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
entry->offset, gpio_device_get_label(gdev));
list_for_each_entry(ref, &entry->refs, list) {
pr_debug("Setting up a shared GPIO entry for %s\n",
fwnode_get_name(ref->fwnode));
ret = gpio_shared_make_adev(gdev, ref);
if (ret)
return ret;
}
}
return 0;
}
void gpio_device_teardown_shared(struct gpio_device *gdev)
{
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) {
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
list_for_each_entry(ref, &entry->refs, list) {
gpiod_remove_lookup_table(ref->lookup);
kfree(ref->lookup->table[0].key);
kfree(ref->lookup);
ref->lookup = NULL;
gpio_shared_remove_adev(&ref->adev);
}
}
}
static void gpio_shared_release(struct kref *kref)
{
struct gpio_shared_entry *entry =
container_of(kref, struct gpio_shared_entry, ref);
struct gpio_shared_desc *shared_desc = entry->shared_desc;
guard(mutex)(&gpio_shared_lock);
gpio_device_put(shared_desc->desc->gdev);
if (shared_desc->can_sleep)
mutex_destroy(&shared_desc->mutex);
kfree(shared_desc);
entry->shared_desc = NULL;
}
static void gpiod_shared_put(void *data)
{
struct gpio_shared_entry *entry = data;
lockdep_assert_not_held(&gpio_shared_lock);
kref_put(&entry->ref, gpio_shared_release);
}
static struct gpio_shared_desc *
gpiod_shared_desc_create(struct gpio_shared_entry *entry)
{
struct gpio_shared_desc *shared_desc;
struct gpio_device *gdev;
shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
if (!shared_desc)
return ERR_PTR(-ENOMEM);
gdev = gpio_device_find_by_fwnode(entry->fwnode);
if (!gdev) {
kfree(shared_desc);
return ERR_PTR(-EPROBE_DEFER);
}
shared_desc->desc = &gdev->descs[entry->offset];
shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
if (shared_desc->can_sleep)
mutex_init(&shared_desc->mutex);
else
spin_lock_init(&shared_desc->spinlock);
return shared_desc;
}
static struct gpio_shared_entry *gpiod_shared_find(struct auxiliary_device *adev)
{
struct gpio_shared_desc *shared_desc;
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
guard(mutex)(&gpio_shared_lock);
list_for_each_entry(entry, &gpio_shared_list, list) {
list_for_each_entry(ref, &entry->refs, list) {
if (adev != &ref->adev)
continue;
if (entry->shared_desc) {
kref_get(&entry->ref);
return entry;
}
shared_desc = gpiod_shared_desc_create(entry);
if (IS_ERR(shared_desc))
return ERR_CAST(shared_desc);
kref_init(&entry->ref);
entry->shared_desc = shared_desc;
pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
dev_name(&adev->dev), gpio_chip_hwgpio(shared_desc->desc),
gpio_device_get_label(shared_desc->desc->gdev));
return entry;
}
}
return ERR_PTR(-ENOENT);
}
struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
{
struct gpio_shared_entry *entry;
int ret;
entry = gpiod_shared_find(to_auxiliary_dev(dev));
if (IS_ERR(entry))
return ERR_CAST(entry);
ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
if (ret)
return ERR_PTR(ret);
return entry->shared_desc;
}
EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
{
list_del(&ref->list);
kfree(ref->con_id);
ida_free(&gpio_shared_ida, ref->dev_id);
fwnode_handle_put(ref->fwnode);
kfree(ref);
}
static void gpio_shared_drop_entry(struct gpio_shared_entry *entry)
{
list_del(&entry->list);
fwnode_handle_put(entry->fwnode);
kfree(entry);
}
/*
* This is only called if gpio_shared_init() fails so it's in fact __init and
* not __exit.
*/
static void __init gpio_shared_teardown(void)
{
struct gpio_shared_entry *entry, *epos;
struct gpio_shared_ref *ref, *rpos;
list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
list_for_each_entry_safe(ref, rpos, &entry->refs, list)
gpio_shared_drop_ref(ref);
gpio_shared_drop_entry(entry);
}
}
static void gpio_shared_free_exclusive(void)
{
struct gpio_shared_entry *entry, *epos;
list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
if (list_count_nodes(&entry->refs) > 1)
continue;
gpio_shared_drop_ref(list_first_entry(&entry->refs,
struct gpio_shared_ref,
list));
gpio_shared_drop_entry(entry);
}
}
static int __init gpio_shared_init(void)
{
int ret;
/* Right now, we only support OF-based systems. */
ret = gpio_shared_of_scan();
if (ret) {
gpio_shared_teardown();
pr_err("Failed to scan OF nodes for shared GPIOs: %d\n", ret);
return ret;
}
gpio_shared_free_exclusive();
pr_debug("Finished scanning firmware nodes for shared GPIOs\n");
return 0;
}
postcore_initcall(gpio_shared_init);

View File

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_GPIO_SHARED_H
#define __LINUX_GPIO_SHARED_H
#include <linux/cleanup.h>
#include <linux/lockdep.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
struct gpio_device;
struct gpio_desc;
struct device;
#if IS_ENABLED(CONFIG_GPIO_SHARED)
int gpio_device_setup_shared(struct gpio_device *gdev);
void gpio_device_teardown_shared(struct gpio_device *gdev);
int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags);
#else
static inline int gpio_device_setup_shared(struct gpio_device *gdev)
{
return 0;
}
static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
unsigned long lflags)
{
return 0;
}
#endif /* CONFIG_GPIO_SHARED */
struct gpio_shared_desc {
struct gpio_desc *desc;
bool can_sleep;
unsigned long cfg;
unsigned int usecnt;
unsigned int highcnt;
union {
struct mutex mutex;
spinlock_t spinlock;
};
};
struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev);
DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc,
if (_T->lock->can_sleep)
mutex_lock(&_T->lock->mutex);
else
spin_lock_irqsave(&_T->lock->spinlock, _T->flags),
if (_T->lock->can_sleep)
mutex_unlock(&_T->lock->mutex);
else
spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags),
unsigned long flags)
static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc)
{
if (shared_desc->can_sleep)
lockdep_assert_held(&shared_desc->mutex);
else
lockdep_assert_held(&shared_desc->spinlock);
}
#endif /* __LINUX_GPIO_SHARED_H */

View File

@ -37,6 +37,7 @@
#include "gpiolib-acpi.h"
#include "gpiolib-cdev.h"
#include "gpiolib-of.h"
#include "gpiolib-shared.h"
#include "gpiolib-swnode.h"
#include "gpiolib-sysfs.h"
#include "gpiolib.h"
@ -1200,6 +1201,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
ret = gpio_device_setup_shared(gdev);
if (ret)
goto err_remove_irqchip;
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
@ -1211,10 +1216,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gpiolib_initialized) {
ret = gpiochip_setup_dev(gdev);
if (ret)
goto err_remove_irqchip;
goto err_teardown_shared;
}
return 0;
err_teardown_shared:
gpio_device_teardown_shared(gdev);
err_remove_irqchip:
gpiochip_irqchip_remove(gc);
err_remove_irqchip_mask:
@ -1283,6 +1291,7 @@ void gpiochip_remove(struct gpio_chip *gc)
/* Numb the device, cancelling all outstanding operations */
rcu_assign_pointer(gdev->chip, NULL);
synchronize_srcu(&gdev->srcu);
gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
of_gpiochip_remove(gc);
@ -3981,6 +3990,26 @@ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
/**
* gpiod_is_shared() - check if this GPIO can be shared by multiple consumers
* @desc: GPIO to inspect
*
* Returns:
* True if this GPIO can be shared by multiple consumers at once. False if it's
* a regular, exclusive GPIO.
*
* Note:
* This function returning true does not mean that this GPIO is currently being
* shared. It means the GPIO core has registered the fact that the firmware
* configuration indicates that it can be shared by multiple consumers and is
* in charge of arbitrating the access.
*/
bool gpiod_is_shared(const struct gpio_desc *desc)
{
return test_bit(GPIOD_FLAG_SHARED_PROXY, &desc->flags);
}
EXPORT_SYMBOL_GPL(gpiod_is_shared);
/**
* gpiod_to_irq() - return the IRQ corresponding to a GPIO
* @desc: gpio whose IRQ will be returned (already requested)
@ -4652,11 +4681,29 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
scoped_guard(srcu, &gpio_devices_srcu) {
desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx,
&flags, &lookupflags);
if (!IS_ERR_OR_NULL(desc) &&
test_bit(GPIOD_FLAG_SHARED, &desc->flags)) {
/*
* We're dealing with a GPIO shared by multiple
* consumers. This is the moment to add the machine
* lookup table for the proxy device as previously
* we only knew the consumer's fwnode.
*/
ret = gpio_shared_add_proxy_lookup(consumer, lookupflags);
if (ret)
return ERR_PTR(ret);
/* Trigger platform lookup for shared GPIO proxy. */
desc = ERR_PTR(-ENOENT);
/* Trigger it even for fwnode-only gpiod_get(). */
platform_lookup_allowed = true;
}
if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
* Either we are not using DT or ACPI, or their lookup
* did not return a result. In that case, use platform
* lookup as a fallback.
* did not return a result or this is a shared GPIO. In
* that case, use platform lookup as a fallback.
*/
dev_dbg(consumer,
"using lookup tables for GPIO lookup\n");
@ -4679,14 +4726,19 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
return ERR_PTR(ret);
/*
* This happens when there are several consumers for
* the same GPIO line: we just return here without
* further initialization. It is a bit of a hack.
* This is necessary to support fixed regulators.
* This happens when there are several consumers for the same
* GPIO line: we just return here without further
* initialization. It's a hack introduced long ago to support
* fixed regulators. We now have a better solution with
* automated scanning where affected platforms just need to
* select the provided Kconfig option.
*
* FIXME: Make this more sane and safe.
* FIXME: Remove the GPIOD_FLAGS_BIT_NONEXCLUSIVE flag after
* making sure all platforms use the new mechanism.
*/
dev_info(consumer, "nonexclusive access to GPIO for %s\n", name);
dev_info(consumer,
"nonexclusive access to GPIO for %s, consider updating your code to using gpio-shared-proxy\n",
name);
return desc;
}

View File

@ -204,6 +204,8 @@ struct gpio_desc {
#define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */
#define GPIOD_FLAG_SHARED_PROXY 21 /* GPIO is a virtual proxy to a physically shared pin. */
/* Connection label */
struct gpio_desc_label __rcu *label;

View File

@ -34,8 +34,6 @@ static void arizona_haptics_work(struct work_struct *work)
struct arizona_haptics,
work);
struct arizona *arizona = haptics->arizona;
struct snd_soc_component *component =
snd_soc_dapm_to_component(arizona->dapm);
int ret;
if (!haptics->arizona->dapm) {
@ -65,7 +63,7 @@ static void arizona_haptics_work(struct work_struct *work)
return;
}
ret = snd_soc_component_enable_pin(component, "HAPTICS");
ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
ret);
@ -80,7 +78,7 @@ static void arizona_haptics_work(struct work_struct *work)
}
} else {
/* This disable sequence will be a noop if already enabled */
ret = snd_soc_component_disable_pin(component, "HAPTICS");
ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
ret);
@ -139,14 +137,12 @@ static int arizona_haptics_play(struct input_dev *input, void *data,
static void arizona_haptics_close(struct input_dev *input)
{
struct arizona_haptics *haptics = input_get_drvdata(input);
struct snd_soc_component *component;
struct snd_soc_dapm_context *dapm = haptics->arizona->dapm;
cancel_work_sync(&haptics->work);
if (haptics->arizona->dapm) {
component = snd_soc_dapm_to_component(haptics->arizona->dapm);
snd_soc_component_disable_pin(component, "HAPTICS");
}
if (dapm)
snd_soc_dapm_disable_pin(dapm, "HAPTICS");
}
static int arizona_haptics_probe(struct platform_device *pdev)

View File

@ -48,7 +48,6 @@ static void arizona_micsupp_check_cp(struct work_struct *work)
struct arizona_micsupp *micsupp =
container_of(work, struct arizona_micsupp, check_cp_work);
struct snd_soc_dapm_context *dapm = *micsupp->dapm;
struct snd_soc_component *component;
const struct regulator_desc *desc = micsupp->desc;
unsigned int val;
int ret;
@ -61,14 +60,11 @@ static void arizona_micsupp_check_cp(struct work_struct *work)
}
if (dapm) {
component = snd_soc_dapm_to_component(dapm);
if ((val & (desc->enable_mask | desc->bypass_mask)) ==
desc->enable_mask)
snd_soc_component_force_enable_pin(component,
"MICSUPP");
snd_soc_dapm_force_enable_pin(dapm, "MICSUPP");
else
snd_soc_component_disable_pin(component, "MICSUPP");
snd_soc_dapm_disable_pin(dapm, "MICSUPP");
snd_soc_dapm_sync(dapm);
}

View File

@ -807,6 +807,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
{
int ret;
struct snd_soc_component *comp;
struct snd_soc_dapm_context *dapm;
struct gbaudio_jack *jack = NULL;
if (!gbcodec) {
@ -815,6 +816,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
}
comp = gbcodec->component;
dapm = snd_soc_component_to_dapm(comp);
mutex_lock(&gbcodec->register_mutex);
@ -833,18 +835,18 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
}
if (module->dapm_widgets)
snd_soc_dapm_new_controls(&comp->dapm, module->dapm_widgets,
snd_soc_dapm_new_controls(dapm, module->dapm_widgets,
module->num_dapm_widgets);
if (module->controls)
snd_soc_add_component_controls(comp, module->controls,
module->num_controls);
if (module->dapm_routes)
snd_soc_dapm_add_routes(&comp->dapm, module->dapm_routes,
snd_soc_dapm_add_routes(dapm, module->dapm_routes,
module->num_dapm_routes);
/* card already instantiated, create widgets here only */
if (comp->card->instantiated) {
gbaudio_dapm_link_component_dai_widgets(comp->card, &comp->dapm);
gbaudio_dapm_link_component_dai_widgets(comp->card, dapm);
#ifdef CONFIG_SND_JACK
/*
* register jack devices for this module
@ -966,9 +968,11 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
#endif
if (module->dapm_routes) {
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(comp);
dev_dbg(comp->dev, "Removing %d routes\n",
module->num_dapm_routes);
snd_soc_dapm_del_routes(&comp->dapm, module->dapm_routes,
snd_soc_dapm_del_routes(dapm, module->dapm_routes,
module->num_dapm_routes);
}
if (module->controls) {
@ -979,9 +983,11 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
module->num_controls);
}
if (module->dapm_widgets) {
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(comp);
dev_dbg(comp->dev, "Removing %d widgets\n",
module->num_dapm_widgets);
gbaudio_dapm_free_controls(&comp->dapm, module->dapm_widgets,
gbaudio_dapm_free_controls(dapm, module->dapm_widgets,
module->num_dapm_widgets);
}

View File

@ -115,12 +115,13 @@ int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
{
int i;
struct snd_soc_dapm_widget *w, *tmp_w;
struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
mutex_lock(&dapm->card->dapm_mutex);
mutex_lock(&card->dapm_mutex);
for (i = 0; i < num; i++) {
/* below logic can be optimized to identify widget pointer */
w = NULL;
list_for_each_entry(tmp_w, &dapm->card->widgets, list) {
list_for_each_entry(tmp_w, &card->widgets, list) {
if (tmp_w->dapm == dapm &&
!strcmp(tmp_w->name, widget->name)) {
w = tmp_w;
@ -128,7 +129,7 @@ int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
}
}
if (!w) {
dev_err(dapm->dev, "%s: widget not found\n",
dev_err(card->dev, "%s: widget not found\n",
widget->name);
widget++;
continue;
@ -136,7 +137,7 @@ int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
widget++;
gbaudio_dapm_free_widget(w);
}
mutex_unlock(&dapm->card->dapm_mutex);
mutex_unlock(&card->dapm_mutex);
return 0;
}

View File

@ -163,7 +163,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_info *info;
struct gbaudio_module_info *module;
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gbcodec = snd_soc_component_get_drvdata(comp);
dev_dbg(comp->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
@ -214,7 +214,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue;
struct gbaudio_module_info *module;
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
struct gb_bundle *bundle;
@ -276,7 +276,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
struct gbaudio_ctl_pvt *data;
struct gb_audio_ctl_elem_value gbvalue;
struct gbaudio_module_info *module;
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
struct gb_bundle *bundle;
@ -380,7 +380,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
struct gbaudio_module_info *module;
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct device *codec_dev = widget->dapm->dev;
struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
struct gb_bundle *bundle;
@ -393,7 +393,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
bundle = to_gb_bundle(module->dev);
if (data->vcount == 2)
dev_warn(widget->dapm->dev,
dev_warn(codec_dev,
"GB: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
@ -429,7 +429,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
struct gbaudio_module_info *module;
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct device *codec_dev = widget->dapm->dev;
struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
struct gb_bundle *bundle;
@ -443,7 +443,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
bundle = to_gb_bundle(module->dev);
if (data->vcount == 2)
dev_warn(widget->dapm->dev,
dev_warn(codec_dev,
"GB: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
@ -543,7 +543,7 @@ static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret, ctl_id;
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct gb_audio_ctl_elem_value gbvalue;
@ -588,7 +588,7 @@ static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret, ctl_id;
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_component_get_drvdata(comp);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct gb_audio_ctl_elem_value gbvalue;
@ -712,7 +712,7 @@ static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct gbaudio_module_info *module;
struct gb_audio_ctl_elem_value gbvalue;
struct device *codec_dev = widget->dapm->dev;
struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct gb_bundle *bundle;
@ -759,7 +759,7 @@ static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct gb_audio_ctl_elem_value gbvalue;
struct gbaudio_module_info *module;
struct device *codec_dev = widget->dapm->dev;
struct device *codec_dev = snd_soc_dapm_to_dev(widget->dapm);
struct gbaudio_codec_info *gb = dev_get_drvdata(codec_dev);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct gb_bundle *bundle;
@ -924,7 +924,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
{
int wid;
int ret;
struct device *codec_dev = w->dapm->dev;
struct device *codec_dev = snd_soc_dapm_to_dev(w->dapm);
struct gbaudio_codec_info *gbcodec = dev_get_drvdata(codec_dev);
struct gbaudio_module_info *module;
struct gb_bundle *bundle;

View File

@ -102,7 +102,7 @@ struct cs_dsp_coeff_ctl {
const char *subname;
unsigned int subname_len;
unsigned int offset;
size_t len;
unsigned int len;
unsigned int type;
unsigned int flags;
unsigned int set:1;
@ -188,8 +188,8 @@ struct cs_dsp {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
char *wmfw_file_name;
char *bin_file_name;
const char *wmfw_file_name;
const char *bin_file_name;
#endif
};

View File

@ -26,21 +26,21 @@ struct cs_dsp_test {
struct cs_dsp_test_local *local;
/* Following members are private */
/* private: Following members are private */
bool saw_bus_write;
};
/**
* struct cs_dsp_mock_alg_def - Info for creating a mock algorithm entry.
*
* @id Algorithm ID.
* @ver; Algorithm version.
* @xm_base_words XM base address in DSP words.
* @xm_size_words XM size in DSP words.
* @ym_base_words YM base address in DSP words.
* @ym_size_words YM size in DSP words.
* @zm_base_words ZM base address in DSP words.
* @zm_size_words ZM size in DSP words.
* @id: Algorithm ID.
* @ver: Algorithm version.
* @xm_base_words: XM base address in DSP words.
* @xm_size_words: XM size in DSP words.
* @ym_base_words: YM base address in DSP words.
* @ym_size_words: YM size in DSP words.
* @zm_base_words: ZM base address in DSP words.
* @zm_size_words: ZM size in DSP words.
*/
struct cs_dsp_mock_alg_def {
unsigned int id;

View File

@ -167,6 +167,8 @@ int gpiod_cansleep(const struct gpio_desc *desc);
int gpiod_to_irq(const struct gpio_desc *desc);
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name);
bool gpiod_is_shared(const struct gpio_desc *desc);
/* Convert between the old gpio_ and new gpiod_ interfaces */
struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc);
@ -520,6 +522,13 @@ static inline int gpiod_set_consumer_name(struct gpio_desc *desc,
return -EINVAL;
}
static inline bool gpiod_is_shared(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(desc);
return false;
}
static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
{
return NULL;

View File

@ -3075,6 +3075,7 @@
#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6
#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff
#define PCI_DEVICE_ID_INTEL_HDA_FCL 0x67a8
#define PCI_DEVICE_ID_INTEL_HDA_NVL_S 0x6e50
#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020

View File

@ -676,7 +676,7 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@ -738,7 +738,7 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@ -970,7 +970,7 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
*/
#define regmap_init_sdw_mbq(sdw, config) \
__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \
sdw, config, NULL)
&sdw->dev, sdw, config, NULL)
/**
* regmap_init_sdw_mbq_cfg() - Initialise MBQ SDW register map with config
@ -983,9 +983,9 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \
#define regmap_init_sdw_mbq_cfg(dev, sdw, config, mbq_config) \
__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \
sdw, config, mbq_config)
dev, sdw, config, mbq_config)
/**
* regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave
@ -1198,12 +1198,13 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
*/
#define devm_regmap_init_sdw_mbq(sdw, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, #config, \
sdw, config, NULL)
&sdw->dev, sdw, config, NULL)
/**
* devm_regmap_init_sdw_mbq_cfg() - Initialise managed MBQ SDW register map with config
*
* @sdw: Device that will be interacted with
* @dev: Device that will be interacted with
* @sdw: SoundWire Device that will be interacted with
* @config: Configuration for register map
* @mbq_config: Properties for the MBQ registers
*
@ -1211,9 +1212,9 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define devm_regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \
__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, \
#config, sdw, config, mbq_config)
#define devm_regmap_init_sdw_mbq_cfg(dev, sdw, config, mbq_config) \
__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, \
#config, dev, sdw, config, mbq_config)
/**
* devm_regmap_init_slimbus() - Initialise managed register map

View File

@ -355,4 +355,6 @@
/* Check the reserved and fixed bits in address */
#define SDW_SDCA_VALID_CTL(reg) (((reg) & (GENMASK(31, 25) | BIT(18) | BIT(13))) == BIT(30))
#define SDW_SDCA_MAX_REGISTER 0x47FFFFFF
#endif /* __SDW_REGISTERS_H */

View File

@ -562,4 +562,22 @@ static inline bool strstarts(const char *str, const char *prefix)
return strncmp(str, prefix, strlen(prefix)) == 0;
}
/**
* strends - Check if a string ends with another string.
* @str - NULL-terminated string to check against @suffix
* @suffix - NULL-terminated string defining the suffix to look for in @str
*
* Returns:
* True if @str ends with @suffix. False in all other cases.
*/
static inline bool strends(const char *str, const char *suffix)
{
unsigned int str_len = strlen(str), suffix_len = strlen(suffix);
if (str_len < suffix_len)
return false;
return !(strcmp(str + str_len - suffix_len, suffix));
}
#endif /* _LINUX_STRING_H_ */

View File

@ -47,21 +47,44 @@ struct cirrus_amp_cal_controls {
int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const struct cirrus_amp_cal_data *data);
int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
struct cirrus_amp_cal_data *data);
int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
u32 temp);
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data);
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
const struct cirrus_amp_cal_data *in_data);
int cs_amp_get_vendor_spkid(struct device *dev);
struct dentry *cs_amp_create_debugfs(struct device *dev);
static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
{
return ((u64)data->calTarget[1] << 32) | data->calTarget[0];
}
struct cs_amp_test_hooks {
efi_status_t (*get_efi_variable)(efi_char16_t *name,
efi_guid_t *guid,
u32 *returned_attr,
unsigned long *size,
void *buf);
efi_status_t (*set_efi_variable)(efi_char16_t *name,
efi_guid_t *guid,
u32 attr,
unsigned long size,
void *buf);
int (*write_cal_coeff)(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const char *ctl_name, u32 val);
};
int (*read_cal_coeff)(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const char *ctl_name, u32 *val);
};
extern const struct cs_amp_test_hooks * const cs_amp_test_hooks;
#endif /* CS_AMP_LIB_H */

View File

@ -9,12 +9,15 @@
#ifndef __CS35L56_H
#define __CS35L56_H
#include <linux/debugfs.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/cs-amp-lib.h>
struct snd_ctl_elem_value;
#define CS35L56_DEVID 0x0000000
#define CS35L56_REVID 0x0000004
#define CS35L56_RELID 0x000000C
@ -62,6 +65,8 @@
#define CS35L56_IRQ1_MASK_8 0x000E0AC
#define CS35L56_IRQ1_MASK_18 0x000E0D4
#define CS35L56_IRQ1_MASK_20 0x000E0DC
#define CS35L56_MIXER_NGATE_CH1_CFG 0x0010004
#define CS35L56_MIXER_NGATE_CH2_CFG 0x0010008
#define CS35L56_DSP_MBOX_1_RAW 0x0011000
#define CS35L56_DSP_VIRTUAL1_MBOX_1 0x0011020
#define CS35L56_DSP_VIRTUAL1_MBOX_2 0x0011024
@ -177,6 +182,9 @@
/* IRQ1_EINT_8 */
#define CS35L56_TEMP_ERR_EINT1_MASK 0x80000000
/* MIXER_NGATE_CHn_CFG */
#define CS35L56_AUX_NGATE_CHn_EN 0x00000001
/* Mixer input sources */
#define CS35L56_INPUT_SRC_NONE 0x00
#define CS35L56_INPUT_SRC_ASP1RX1 0x08
@ -243,6 +251,7 @@
#define CS35L56_MBOX_CMD_AUDIO_PLAY 0x0B000001
#define CS35L56_MBOX_CMD_AUDIO_PAUSE 0x0B000002
#define CS35L56_MBOX_CMD_AUDIO_REINIT 0x0B000003
#define CS35L56_MBOX_CMD_AUDIO_CALIBRATION 0x0B000006
#define CS35L56_MBOX_CMD_HIBERNATE_NOW 0x02000001
#define CS35L56_MBOX_CMD_WAKEUP 0x02000002
#define CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE 0x02000003
@ -258,12 +267,22 @@
#define CS35L56_PS3_POLL_US 500
#define CS35L56_PS3_TIMEOUT_US 300000
#define CS35L56_CAL_STATUS_SUCCESS 1
#define CS35L56_CAL_STATUS_OUT_OF_RANGE 3
#define CS35L56_CAL_SET_STATUS_UNKNOWN 0
#define CS35L56_CAL_SET_STATUS_DEFAULT 1
#define CS35L56_CAL_SET_STATUS_SET 2
#define CS35L56_CONTROL_PORT_READY_US 2200
#define CS35L56_HALO_STATE_POLL_US 1000
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
#define CS35L56_RESET_PULSE_MIN_US 1100
#define CS35L56_WAKE_HOLD_TIME_US 1000
#define CS35L56_CALIBRATION_POLL_US (100 * USEC_PER_MSEC)
#define CS35L56_CALIBRATION_TIMEOUT_US (5 * USEC_PER_SEC)
#define CS35L56_SDW1_PLAYBACK_PORT 1
#define CS35L56_SDW1_CAPTURE_PORT 3
@ -291,9 +310,16 @@ struct cs35l56_fw_reg {
unsigned int posture_number;
};
struct cs35l56_cal_debugfs_fops {
const struct debugfs_short_fops calibrate;
const struct debugfs_short_fops cal_temperature;
const struct debugfs_short_fops cal_data;
};
struct cs35l56_base {
struct device *dev;
struct regmap *regmap;
struct cs_dsp *dsp;
int irq;
struct mutex irq_lock;
u8 type;
@ -304,11 +330,14 @@ struct cs35l56_base {
bool can_hibernate;
bool cal_data_valid;
s8 cal_index;
u8 num_amps;
struct cirrus_amp_cal_data cal_data;
struct gpio_desc *reset_gpio;
struct cs35l56_spi_payload *spi_payload_buf;
const struct cs35l56_fw_reg *fw_reg;
const struct cirrus_amp_cal_controls *calibration_controls;
struct dentry *debugfs;
u64 silicon_uid;
};
static inline bool cs35l56_is_otp_register(unsigned int reg)
@ -340,6 +369,7 @@ extern const struct regmap_config cs35l63_regmap_i2c;
extern const struct regmap_config cs35l63_regmap_sdw;
extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;
extern const char * const cs35l56_cal_set_status_text[3];
extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
@ -358,8 +388,28 @@ int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base);
int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base,
const struct cirrus_amp_cal_data *data);
ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base,
const char __user *from, size_t count,
loff_t *ppos);
ssize_t cs35l56_cal_ambient_debugfs_write(struct cs35l56_base *cs35l56_base,
const char __user *from, size_t count,
loff_t *ppos);
ssize_t cs35l56_cal_data_debugfs_read(struct cs35l56_base *cs35l56_base,
char __user *to, size_t count,
loff_t *ppos);
ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
const char __user *from, size_t count,
loff_t *ppos);
void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base,
const struct cs35l56_cal_debugfs_fops *fops);
void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base);
int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base,
struct snd_ctl_elem_value *uvalue);
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
bool *fw_missing, unsigned int *fw_version);
void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);

View File

@ -12,19 +12,24 @@
#include <linux/types.h>
#include <linux/kconfig.h>
struct acpi_table_swft;
struct fwnode_handle;
struct sdw_slave;
struct sdca_dev;
#define SDCA_MAX_FUNCTION_COUNT 8
/**
* struct sdca_function_desc - short descriptor for an SDCA Function
* @node: firmware node for the Function.
* @func_dev: pointer to SDCA function device.
* @name: Human-readable string.
* @type: Function topology type.
* @adr: ACPI address (used for SDCA register access).
*/
struct sdca_function_desc {
struct fwnode_handle *node;
struct sdca_dev *func_dev;
const char *name;
u32 type;
u8 adr;
@ -37,11 +42,13 @@ struct sdca_function_desc {
* @num_functions: Total number of supported SDCA functions. Invalid/unsupported
* functions will be skipped.
* @function: Array of function descriptors.
* @swft: Pointer to the SWFT table, if available.
*/
struct sdca_device_data {
u32 interface_revision;
int num_functions;
struct sdca_function_desc function[SDCA_MAX_FUNCTION_COUNT];
struct acpi_table_swft *swft;
};
enum sdca_quirk {
@ -52,17 +59,29 @@ enum sdca_quirk {
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_swft(struct sdw_slave *slave);
void sdca_lookup_interface_revision(struct sdw_slave *slave);
bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk);
int sdca_dev_register_functions(struct sdw_slave *slave);
void sdca_dev_unregister_functions(struct sdw_slave *slave);
#else
static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
static inline void sdca_lookup_swft(struct sdw_slave *slave) {}
static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
{
return false;
}
static inline int sdca_dev_register_functions(struct sdw_slave *slave)
{
return 0;
}
static inline void sdca_dev_unregister_functions(struct sdw_slave *slave) {}
#endif
#endif

105
include/sound/sdca_fdl.h Normal file
View File

@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright (C) 2025 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef __SDCA_FDL_H__
#define __SDCA_FDL_H__
#include <linux/completion.h>
#include <linux/workqueue.h>
struct device;
struct regmap;
struct sdca_fdl_set;
struct sdca_function_data;
struct sdca_interrupt;
struct sdca_interrupt_info;
/**
* struct fdl_state - FDL state structure to keep data between interrupts
* @begin: Completion indicating the start of an FDL download cycle.
* @done: Completion indicating the end of an FDL download cycle.
* @timeout: Delayed work used for timing out UMP transactions.
* @lock: Mutex to protect between the timeout work and IRQ handlers.
* @interrupt: Pointer to the interrupt struct to which this FDL is attached.
* @set: Pointer to the FDL set currently being downloaded.
* @file_index: Index of the current file being processed.
*/
struct fdl_state {
struct completion begin;
struct completion done;
struct delayed_work timeout;
struct mutex lock;
struct sdca_interrupt *interrupt;
struct sdca_fdl_set *set;
int file_index;
};
#define SDCA_CTL_XU_FDLH_COMPLETE 0
#define SDCA_CTL_XU_FDLH_MORE_FILES SDCA_CTL_XU_FDLH_SET_IN_PROGRESS
#define SDCA_CTL_XU_FDLH_FILE_AVAILABLE (SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \
SDCA_CTL_XU_FDLH_SET_IN_PROGRESS)
#define SDCA_CTL_XU_FDLH_MASK (SDCA_CTL_XU_FDLH_TRANSFERRED_CHUNK | \
SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \
SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \
SDCA_CTL_XU_FDLH_RESET_ACK | \
SDCA_CTL_XU_FDLH_REQ_ABORT)
#define SDCA_CTL_XU_FDLD_COMPLETE 0
#define SDCA_CTL_XU_FDLD_FILE_OK (SDCA_CTL_XU_FDLH_TRANSFERRED_FILE | \
SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \
SDCA_CTL_XU_FDLD_ACK_TRANSFER | \
SDCA_CTL_XU_FDLD_NEEDS_SET)
#define SDCA_CTL_XU_FDLD_MORE_FILES_OK (SDCA_CTL_XU_FDLH_SET_IN_PROGRESS | \
SDCA_CTL_XU_FDLD_ACK_TRANSFER | \
SDCA_CTL_XU_FDLD_NEEDS_SET)
#define SDCA_CTL_XU_FDLD_MASK (SDCA_CTL_XU_FDLD_REQ_RESET | \
SDCA_CTL_XU_FDLD_REQ_ABORT | \
SDCA_CTL_XU_FDLD_ACK_TRANSFER | \
SDCA_CTL_XU_FDLD_NEEDS_SET)
#if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL)
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt);
int sdca_fdl_process(struct sdca_interrupt *interrupt);
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
struct sdca_interrupt_info *info);
int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
struct regmap *regmap);
#else
static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
{
return 0;
}
static inline int sdca_fdl_process(struct sdca_interrupt *interrupt)
{
return 0;
}
static inline int sdca_fdl_sync(struct device *dev,
struct sdca_function_data *function,
struct sdca_interrupt_info *info)
{
return 0;
}
static inline int sdca_reset_function(struct device *dev,
struct sdca_function_data *function,
struct regmap *regmap)
{
return 0;
}
#endif // CONFIG_SND_SOC_SDCA_FDL
#endif // __SDCA_FDL_H__

View File

@ -13,6 +13,7 @@
#include <linux/types.h>
#include <linux/hid.h>
struct acpi_table_swft;
struct device;
struct sdca_entity;
struct sdca_function_desc;
@ -63,6 +64,7 @@ struct sdca_function_desc;
* @SDCA_FUNCTION_TYPE_RJ: Retaskable jack.
* @SDCA_FUNCTION_TYPE_SIMPLE_JACK: Subset of UAJ.
* @SDCA_FUNCTION_TYPE_HID: Human Interface Device, for e.g. buttons.
* @SDCA_FUNCTION_TYPE_COMPANION_AMP: Sources audio from another amp.
* @SDCA_FUNCTION_TYPE_IMP_DEF: Implementation-defined function.
*
* SDCA Function Types from SDCA specification v1.0a Section 5.1.2
@ -82,6 +84,7 @@ enum sdca_function_type {
SDCA_FUNCTION_TYPE_RJ = 0x07,
SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08,
SDCA_FUNCTION_TYPE_HID = 0x0A,
SDCA_FUNCTION_TYPE_COMPANION_AMP = 0x0B,
SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F,
};
@ -95,6 +98,7 @@ enum sdca_function_type {
#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
#define SDCA_FUNCTION_TYPE_COMPANION_AMP_NAME "CompanionAmp"
#define SDCA_FUNCTION_TYPE_IMP_DEF_NAME "ImplementationDefined"
/**
@ -133,6 +137,32 @@ struct sdca_init_write {
#define SDCA_CTL_TYPE_S(ent, sel) SDCA_CTL_TYPE(SDCA_ENTITY_TYPE_##ent, \
SDCA_CTL_##ent##_##sel)
/**
* enum sdca_messageoffset_range - Column definitions UMP MessageOffset
*/
enum sdca_messageoffset_range {
SDCA_MESSAGEOFFSET_BUFFER_START_ADDRESS = 0,
SDCA_MESSAGEOFFSET_BUFFER_LENGTH = 1,
SDCA_MESSAGEOFFSET_UMP_MODE = 2,
SDCA_MESSAGEOFFSET_NCOLS = 3,
};
/**
* enum sdca_ump_mode - SDCA UMP Mode
*/
enum sdca_ump_mode {
SDCA_UMP_MODE_DIRECT = 0x00,
SDCA_UMP_MODE_INDIRECT = 0x01,
};
/**
* enum sdca_ump_owner - SDCA UMP Owner
*/
enum sdca_ump_owner {
SDCA_UMP_OWNER_HOST = 0x00,
SDCA_UMP_OWNER_DEVICE = 0x01,
};
/**
* enum sdca_it_controls - SDCA Controls for Input Terminal
*
@ -258,6 +288,27 @@ enum sdca_xu_controls {
SDCA_CTL_XU_FDL_STATUS = 0x14,
SDCA_CTL_XU_FDL_SET_INDEX = 0x15,
SDCA_CTL_XU_FDL_HOST_REQUEST = 0x16,
/* FDL Status Host->Device bit definitions */
SDCA_CTL_XU_FDLH_TRANSFERRED_CHUNK = BIT(0),
SDCA_CTL_XU_FDLH_TRANSFERRED_FILE = BIT(1),
SDCA_CTL_XU_FDLH_SET_IN_PROGRESS = BIT(2),
SDCA_CTL_XU_FDLH_RESET_ACK = BIT(4),
SDCA_CTL_XU_FDLH_REQ_ABORT = BIT(5),
/* FDL Status Device->Host bit definitions */
SDCA_CTL_XU_FDLD_REQ_RESET = BIT(4),
SDCA_CTL_XU_FDLD_REQ_ABORT = BIT(5),
SDCA_CTL_XU_FDLD_ACK_TRANSFER = BIT(6),
SDCA_CTL_XU_FDLD_NEEDS_SET = BIT(7),
};
/**
* enum sdca_set_index_range - Column definitions UMP SetIndex
*/
enum sdca_fdl_set_index_range {
SDCA_FDL_SET_INDEX_SET_NUMBER = 0,
SDCA_FDL_SET_INDEX_FILE_SET_ID = 1,
SDCA_FDL_SET_INDEX_NCOLS = 2,
};
/**
@ -542,6 +593,9 @@ enum sdca_entity0_controls {
SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION = BIT(5),
SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET = BIT(6),
SDCA_CTL_ENTITY_0_FUNCTION_BUSY = BIT(7),
/* Function Action Bits */
SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW = BIT(0),
};
#define SDCA_CTL_MIC_BIAS_NAME "Mic Bias"
@ -557,7 +611,7 @@ enum sdca_entity0_controls {
#define SDCA_CTL_NDAI_PACKETTYPE_NAME "NDAI Packet Type"
#define SDCA_CTL_MIXER_NAME "Mixer"
#define SDCA_CTL_SELECTOR_NAME "Selector"
#define SDCA_CTL_MUTE_NAME "Mute"
#define SDCA_CTL_MUTE_NAME "Channel"
#define SDCA_CTL_CHANNEL_VOLUME_NAME "Channel Volume"
#define SDCA_CTL_AGC_NAME "AGC"
#define SDCA_CTL_BASS_BOOST_NAME "Bass Boost"
@ -771,6 +825,7 @@ struct sdca_control {
u8 layers;
bool deferrable;
bool is_volatile;
bool has_default;
bool has_fixed;
};
@ -1089,6 +1144,27 @@ struct sdca_entity_hide {
struct hid_descriptor hid_desc;
};
/**
* enum sdca_xu_reset_machanism - SDCA FDL Resets
*/
enum sdca_xu_reset_mechanism {
SDCA_XU_RESET_FUNCTION = 0x0,
SDCA_XU_RESET_DEVICE = 0x1,
SDCA_XU_RESET_BUS = 0x2,
};
/**
* struct sdca_entity_xu - information specific to XU Entities
* @max_delay: the maximum time in microseconds allowed for the Device
* to change the ownership from Device to Host
* @reset_mechanism: indicates the type of reset that can be requested
* the end of an FDL.
*/
struct sdca_entity_xu {
unsigned int max_delay;
enum sdca_xu_reset_mechanism reset_mechanism;
};
/**
* struct sdca_entity - information for one SDCA Entity
* @label: String such as "OT 12".
@ -1105,6 +1181,7 @@ struct sdca_entity_hide {
* @pde: Power Domain Entity specific Entity properties.
* @ge: Group Entity specific Entity properties.
* @hide: HIDE Entity specific Entity properties.
* @xu: XU Entity specific Entity properties.
*/
struct sdca_entity {
const char *label;
@ -1122,6 +1199,7 @@ struct sdca_entity {
struct sdca_entity_pde pde;
struct sdca_entity_ge ge;
struct sdca_entity_hide hide;
struct sdca_entity_xu xu;
};
};
@ -1288,6 +1366,42 @@ enum sdca_cluster_range {
SDCA_CLUSTER_NCOLS = 2,
};
/**
* struct sdca_fdl_file - information about a file from a fileset used in FDL
* @vendor_id: Vendor ID of the file.
* @file_id: File ID of the file.
* @fdl_offset: Offset information for FDL.
*/
struct sdca_fdl_file {
u16 vendor_id;
u32 file_id;
u32 fdl_offset;
};
/**
* struct sdca_fdl_set - information about a set of files used in FDL
* @files: Array of files in this FDL set.
* @num_files: Number of files in this FDL set.
* @id: ID of the FDL set.
*/
struct sdca_fdl_set {
struct sdca_fdl_file *files;
int num_files;
u32 id;
};
/**
* struct sdca_fdl_data - information about a function's FDL data
* @swft: Pointer to the SoundWire File Table.
* @sets: Array of FDL sets used by this function.
* @num_sets: Number of FDL sets used by this function.
*/
struct sdca_fdl_data {
struct acpi_table_swft *swft;
struct sdca_fdl_set *sets;
int num_sets;
};
/**
* struct sdca_function_data - top-level information for one SDCA function
* @desc: Pointer to short descriptor from initial parsing.
@ -1299,6 +1413,9 @@ enum sdca_cluster_range {
* @num_clusters: Number of Channel Clusters reported in this Function.
* @busy_max_delay: Maximum Function busy delay in microseconds, before an
* error should be reported.
* @reset_max_delay: Maximum Function reset delay in microseconds, before an
* error should be reported.
* @fdl_data: FDL data for this Function, if available.
*/
struct sdca_function_data {
struct sdca_function_desc *desc;
@ -1311,6 +1428,9 @@ struct sdca_function_data {
int num_clusters;
unsigned int busy_max_delay;
unsigned int reset_max_delay;
struct sdca_fdl_data fdl_data;
};
static inline u32 sdca_range(struct sdca_control_range *range,
@ -1332,10 +1452,12 @@ static inline u32 sdca_range_search(struct sdca_control_range *range,
return 0;
}
int sdca_parse_function(struct device *dev,
int sdca_parse_function(struct device *dev, struct sdw_slave *sdw,
struct sdca_function_desc *desc,
struct sdca_function_data *function);
const char *sdca_find_terminal_name(enum sdca_terminal_type type);
struct sdca_control *sdca_selector_find_control(struct device *dev,
struct sdca_entity *entity,
const int sel);

View File

@ -8,14 +8,27 @@
#ifndef __SDCA_HID_H__
#define __SDCA_HID_H__
#include <linux/types.h>
#include <linux/hid.h>
struct device;
struct sdw_slave;
struct sdca_entity;
struct sdca_interrupt;
#if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID)
int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity);
int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw,
struct sdca_entity *entity);
int sdca_hid_process_report(struct sdca_interrupt *interrupt);
#else
static inline int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity)
static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw,
struct sdca_entity *entity)
{
return 0;
}
static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt)
{
return 0;
}

View File

@ -23,18 +23,23 @@ struct sdca_function_data;
/**
* struct sdca_interrupt - contains information about a single SDCA interrupt
* @name: The name of the interrupt.
* @dev: Pointer to the Function device.
* @device_regmap: Pointer to the IRQ regmap.
* @function_regmap: Pointer to the SDCA Function regmap.
* @component: Pointer to the ASoC component owns the interrupt.
* @function: Pointer to the Function that the interrupt is associated with.
* @entity: Pointer to the Entity that the interrupt is associated with.
* @control: Pointer to the Control that the interrupt is associated with.
* @priv: Pointer to private data for use by the handler.
* @externally_requested: Internal flag used to check if a client driver has
* already requested the interrupt, for custom handling, allowing the core to
* skip handling this interrupt.
* @irq: IRQ number allocated to this interrupt, also used internally to track
* the IRQ being assigned.
*/
struct sdca_interrupt {
const char *name;
struct device *dev;
struct regmap *device_regmap;
struct regmap *function_regmap;
struct snd_soc_component *component;
struct sdca_function_data *function;
struct sdca_entity *entity;
@ -42,7 +47,7 @@ struct sdca_interrupt {
void *priv;
bool externally_requested;
int irq;
};
/**
@ -64,11 +69,15 @@ struct sdca_interrupt_info {
int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *interrupt_info,
int sdca_irq, const char *name, irq_handler_t handler,
void *data);
int sdca_irq_data_populate(struct snd_soc_component *component,
int sdca_irq_data_populate(struct device *dev, struct regmap *function_regmap,
struct snd_soc_component *component,
struct sdca_function_data *function,
struct sdca_entity *entity,
struct sdca_control *control,
struct sdca_interrupt *interrupt);
int sdca_irq_populate_early(struct device *dev, struct regmap *function_regmap,
struct sdca_function_data *function,
struct sdca_interrupt_info *info);
int sdca_irq_populate(struct sdca_function_data *function,
struct snd_soc_component *component,
struct sdca_interrupt_info *info);

View File

@ -27,5 +27,7 @@ int sdca_regmap_populate_constants(struct device *dev, struct sdca_function_data
int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
struct sdca_function_data *function);
int sdca_regmap_write_init(struct device *dev, struct regmap *regmap,
struct sdca_function_data *function);
#endif // __SDCA_REGMAP_H__

50
include/sound/sdca_ump.h Normal file
View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright (C) 2025 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef __SDCA_UMP_H__
#define __SDCA_UMP_H__
struct regmap;
struct sdca_control;
struct sdca_entity;
struct sdca_function_data;
struct snd_soc_component;
struct delayed_work;
int sdca_ump_get_owner_host(struct device *dev,
struct regmap *function_regmap,
struct sdca_function_data *function,
struct sdca_entity *entity,
struct sdca_control *control);
int sdca_ump_set_owner_device(struct device *dev,
struct regmap *function_regmap,
struct sdca_function_data *function,
struct sdca_entity *entity,
struct sdca_control *control);
int sdca_ump_read_message(struct device *dev,
struct regmap *device_regmap,
struct regmap *function_regmap,
struct sdca_function_data *function,
struct sdca_entity *entity,
unsigned int offset_sel, unsigned int length_sel,
void **msg);
int sdca_ump_write_message(struct device *dev,
struct regmap *device_regmap,
struct regmap *function_regmap,
struct sdca_function_data *function,
struct sdca_entity *entity,
unsigned int offset_sel, unsigned int msg_offset,
unsigned int length_sel,
void *msg, int msg_len);
void sdca_ump_cancel_timeout(struct delayed_work *work);
void sdca_ump_schedule_timeout(struct delayed_work *work,
unsigned int timeout_us);
#endif // __SDCA_UMP_H__

View File

@ -34,6 +34,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_sdw_machines[];
@ -46,6 +47,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_sdw_machines[];
/*
* generic table used for HDA codec-based platforms, possibly with

View File

@ -114,8 +114,8 @@ struct snd_soc_acpi_endpoint {
* @name_prefix: string used for codec controls
*/
struct snd_soc_acpi_adr_device {
const u64 adr;
const u8 num_endpoints;
u64 adr;
u8 num_endpoints;
const struct snd_soc_acpi_endpoint *endpoints;
const char *name_prefix;
};
@ -131,8 +131,8 @@ struct snd_soc_acpi_adr_device {
*/
struct snd_soc_acpi_link_adr {
const u32 mask;
const u32 num_adr;
u32 mask;
u32 num_adr;
const struct snd_soc_acpi_adr_device *adr_d;
};

View File

@ -319,6 +319,13 @@ struct platform_device;
#define SOC_VALUE_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put)
#define SOC_ENUM_EXT_ACC(xname, xenum, xhandler_get, xhandler_put, xaccess) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = xaccess, \
.info = snd_soc_info_enum_double, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum }
#define SND_SOC_BYTES(xname, xbase, xregs) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
@ -331,6 +338,13 @@ struct platform_device;
.put = xhandler_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_E_ACC(xname, xbase, xregs, xhandler_get, xhandler_put, xaccess) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = xaccess, \
.info = snd_soc_bytes_info, .get = xhandler_get, \
.put = xhandler_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@ -1225,6 +1239,7 @@ struct soc_mixer_control {
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
unsigned int sdca_q78:1;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
#endif
@ -1305,22 +1320,6 @@ static inline unsigned int snd_soc_enum_item_to_val(const struct soc_enum *e,
return e->values[item];
}
/**
* snd_soc_kcontrol_component() - Returns the component that registered the
* control
* @kcontrol: The control for which to get the component
*
* Note: This function will work correctly if the control has been registered
* for a component. With snd_soc_add_codec_controls() or via table based
* setup for either a CODEC or component driver. Otherwise the behavior is
* undefined.
*/
static inline struct snd_soc_component *snd_soc_kcontrol_component(
struct snd_kcontrol *kcontrol)
{
return snd_kcontrol_chip(kcontrol);
}
int snd_soc_util_init(void);
void snd_soc_util_exit(void);
@ -1482,22 +1481,22 @@ static inline void _snd_soc_dapm_mutex_assert_held_c(struct snd_soc_card *card)
static inline void _snd_soc_dapm_mutex_lock_root_d(struct snd_soc_dapm_context *dapm)
{
_snd_soc_dapm_mutex_lock_root_c(dapm->card);
_snd_soc_dapm_mutex_lock_root_c(snd_soc_dapm_to_card(dapm));
}
static inline void _snd_soc_dapm_mutex_lock_d(struct snd_soc_dapm_context *dapm)
{
_snd_soc_dapm_mutex_lock_c(dapm->card);
_snd_soc_dapm_mutex_lock_c(snd_soc_dapm_to_card(dapm));
}
static inline void _snd_soc_dapm_mutex_unlock_d(struct snd_soc_dapm_context *dapm)
{
_snd_soc_dapm_mutex_unlock_c(dapm->card);
_snd_soc_dapm_mutex_unlock_c(snd_soc_dapm_to_card(dapm));
}
static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context *dapm)
{
_snd_soc_dapm_mutex_assert_held_c(dapm->card);
_snd_soc_dapm_mutex_assert_held_c(snd_soc_dapm_to_card(dapm));
}
#define snd_soc_dapm_mutex_lock_root(x) _Generic((x), \

View File

@ -13,6 +13,7 @@
#include <sound/soc-acpi.h>
#define SOC_SDW_MAX_DAI_NUM 8
#define SOC_SDW_MAX_AUX_NUM 2
#define SOC_SDW_MAX_NO_PROPS 2
#define SOC_SDW_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
@ -45,6 +46,7 @@ struct asoc_sdw_codec_info;
struct asoc_sdw_dai_info {
const bool direction[2]; /* playback & capture support */
const char *codec_name;
const char *dai_name;
const char *component_name;
const int dai_type;
@ -64,16 +66,22 @@ struct asoc_sdw_dai_info {
bool quirk_exclude;
};
struct asoc_sdw_aux_info {
const char *codec_name;
};
struct asoc_sdw_codec_info {
const int part_id;
const int version_id;
const char *codec_name;
const char *name_prefix;
int amp_num;
const u8 acpi_id[ACPI_ID_LEN];
const bool ignore_internal_dmic;
const struct snd_soc_ops *ops;
struct asoc_sdw_dai_info dais[SOC_SDW_MAX_DAI_NUM];
const int dai_num;
struct asoc_sdw_aux_info auxs[SOC_SDW_MAX_AUX_NUM];
const int aux_num;
int (*codec_card_late_probe)(struct snd_soc_card *card);
@ -130,7 +138,7 @@ int asoc_sdw_hw_free(struct snd_pcm_substream *substream);
void asoc_sdw_shutdown(struct snd_pcm_substream *substream);
const char *asoc_sdw_get_codec_name(struct device *dev,
const struct asoc_sdw_codec_info *codec_info,
const struct asoc_sdw_dai_info *dai_info,
const struct snd_soc_acpi_link_adr *adr_link,
int adr_index);
@ -164,12 +172,15 @@ int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *d
int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
const struct snd_soc_ops *ops);
int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends);
int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card,
int *num_devs, int *num_ends, int *num_aux);
struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
const struct snd_soc_acpi_endpoint *new);
int asoc_sdw_get_dai_type(u32 type);
int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
struct snd_soc_aux_dev *soc_aux,
struct asoc_sdw_dailink *soc_dais,
struct asoc_sdw_endpoint *soc_ends,
int *num_devs);
@ -246,6 +257,8 @@ int asoc_sdw_cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_da
int asoc_sdw_cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l43_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l45_hs_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs42l45_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
/* TI */

View File

@ -117,14 +117,20 @@ enum audio_device {
TAS2120,
TAS2320,
TAS2563,
TAS2568,
TAS2570,
TAS2572,
TAS2574,
TAS2781,
TAS5802,
TAS5806M,
TAS5806MD,
TAS5815,
TAS5822,
TAS5825,
TAS5827,
TAS5828,
TAS5830,
TAS_OTHERS,
};
@ -197,7 +203,6 @@ struct tasdevice_priv {
struct acoustic_data acou_data;
#endif
struct tasdevice_fw *fmw;
struct gpio_desc *speaker_id;
struct gpio_desc *reset;
struct mutex codec_lock;
struct regmap *regmap;
@ -215,6 +220,7 @@ struct tasdevice_priv {
unsigned int magic_num;
unsigned int chip_id;
unsigned int sysclk;
int speaker_id;
int irq;
int cur_prog;

View File

@ -27,8 +27,8 @@ DECLARE_EVENT_CLASS(snd_soc_dapm,
TP_ARGS(dapm, val),
TP_STRUCT__entry(
__string( card_name, dapm->card->name)
__string( comp_name, dapm->component ? dapm->component->name : "(none)")
__string( card_name, snd_soc_dapm_to_card(dapm)->name)
__string( comp_name, snd_soc_dapm_to_component(dapm) ? snd_soc_dapm_to_component(dapm)->name : "(none)")
__field( int, val)
),

View File

@ -21,6 +21,7 @@ enum avs_tplg_token {
AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8,
AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 = 9,
AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32 = 10,
AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32 = 11,
/* struct avs_tplg_library */
AVS_TKN_LIBRARY_ID_U32 = 101,
@ -124,6 +125,7 @@ enum avs_tplg_token {
AVS_TKN_MOD_KCONTROL_ID_U32 = 1707,
AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32 = 1708,
AVS_TKN_MOD_INIT_CONFIG_ID_U32 = 1709,
AVS_TKN_MOD_NHLT_CONFIG_ID_U32 = 1710,
/* struct avs_tplg_path_template */
AVS_TKN_PATH_TMPL_ID_U32 = 1801,
@ -160,6 +162,10 @@ enum avs_tplg_token {
AVS_TKN_INIT_CONFIG_ID_U32 = 2401,
AVS_TKN_INIT_CONFIG_PARAM_U8 = 2402,
AVS_TKN_INIT_CONFIG_LENGTH_U32 = 2403,
/* struct avs_tplg_nhlt_config */
AVS_TKN_NHLT_CONFIG_ID_U32 = 2501,
AVS_TKN_NHLT_CONFIG_SIZE_U32 = 2502,
};
#endif

View File

@ -602,6 +602,18 @@ static void string_test_memtostr(struct kunit *test)
KUNIT_EXPECT_EQ(test, dest[7], '\0');
}
static void string_test_strends(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
KUNIT_EXPECT_FALSE(test, strends("", "foo"));
KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
KUNIT_EXPECT_TRUE(test, strends("", ""));
}
static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_memset16),
KUNIT_CASE(string_test_memset32),
@ -623,6 +635,7 @@ static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_strlcat),
KUNIT_CASE(string_test_strtomem),
KUNIT_CASE(string_test_memtostr),
KUNIT_CASE(string_test_strends),
{}
};

View File

@ -46,11 +46,14 @@ static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id,
* @id_mask: Mask that is applied to the device ID before comparing to @id
*
* This function resets the AC'97 device. If @try_warm is true the function
* first performs a warm reset. If the warm reset is successful the function
* returns 1. Otherwise or if @try_warm is false the function issues cold reset
* followed by a warm reset. If this is successful the function returns 0,
* otherwise a negative error code. If @id is 0 any valid device ID will be
* accepted, otherwise only the ID that matches @id and @id_mask is accepted.
* first performs a warm reset. If @try_warm is false the function issues
* cold reset followed by a warm reset. If @id is 0 any valid device ID
* will be accepted, otherwise only the ID that matches @id and @id_mask
* is accepted.
* Returns:
* * %1 - if warm reset is successful
* * %0 - if cold reset and warm reset is successful
* * %-ENODEV - if @id and @id_mask not matching
*/
int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
unsigned int id_mask)

View File

@ -379,7 +379,7 @@ int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e,
* in console or for audio devices. Assume the highest speakers
* configuration, to _not_ prohibit multi-channel audio playback.
*/
if (!e->spk_alloc)
if (!e->spk_alloc && e->sad_count)
e->spk_alloc = 0xffff;
return 0;

View File

@ -2106,13 +2106,11 @@ EXPORT_SYMBOL(snd_rawmidi_set_ops);
static int __init alsa_rawmidi_init(void)
{
snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl);
#ifdef CONFIG_SND_OSSEMUL
{ int i;
/* check device map table */
for (i = 0; i < SNDRV_CARDS; i++) {
for (int i = 0; i < SNDRV_CARDS; i++) {
if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n",
i, midi_map[i]);
@ -2124,7 +2122,6 @@ static int __init alsa_rawmidi_init(void)
amidi_map[i] = 1;
}
}
}
#endif /* CONFIG_SND_OSSEMUL */
return 0;
}

View File

@ -696,10 +696,10 @@ static int setup_patt_bufs(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(patt_bufs); i++) {
patt_bufs[i].buf = kzalloc(MAX_PATTERN_LEN, GFP_KERNEL);
patt_bufs[i].buf = kmalloc(MAX_PATTERN_LEN, GFP_KERNEL);
if (!patt_bufs[i].buf)
break;
strcpy(patt_bufs[i].buf, DEFAULT_PATTERN);
strscpy_pad(patt_bufs[i].buf, DEFAULT_PATTERN, MAX_PATTERN_LEN);
patt_bufs[i].len = DEFAULT_PATTERN_LEN;
}

View File

@ -2,5 +2,5 @@
snd-dice-y := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \
dice-harman.o dice-focusrite.o dice-weiss.o
dice-harman.o dice-focusrite.o dice-weiss.o dice-teac.o
obj-$(CONFIG_SND_DICE) += snd-dice.o

View File

@ -116,7 +116,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
break;
base_offset += EXT_APP_STREAM_ENTRIES;
stream_count = be32_to_cpu(reg[0]);
stream_count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
err = read_stream_entries(dice, section_addr, base_offset,
stream_count, mode,
dice->tx_pcm_chs,
@ -125,7 +125,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
break;
base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
stream_count = be32_to_cpu(reg[1]);
stream_count = min_t(unsigned int, be32_to_cpu(reg[1]), MAX_STREAMS);
err = read_stream_entries(dice, section_addr, base_offset,
stream_count,
mode, dice->rx_pcm_chs,

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
// dice-teac.c - a part of driver for DICE based devices
//
// Copyright (c) 2025 Takashi Sakamoto
#include "dice.h"
int snd_dice_detect_teac_formats(struct snd_dice *dice)
{
__be32 reg;
u32 data;
int err;
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg, sizeof(reg));
if (err < 0)
return err;
dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16;
dice->tx_midi_ports[0] = 1;
data = be32_to_cpu(reg);
if (data > 1) {
dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16;
dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16;
}
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg, sizeof(reg));
if (err < 0)
return err;
dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16;
dice->rx_midi_ports[0] = 1;
data = be32_to_cpu(reg);
if (data > 1) {
dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16;
dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16;
}
return 0;
}

View File

@ -22,6 +22,7 @@ MODULE_LICENSE("GPL");
#define OUI_PRESONUS 0x000a92
#define OUI_HARMAN 0x000fd7
#define OUI_AVID 0x00a07e
#define OUI_TEAC 0x00022e
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
@ -458,6 +459,18 @@ static const struct ieee1394_device_id dice_id_table[] = {
.match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE,
},
// Tascam IF-FW/DM MkII for DM-3200 and DM-4800.
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID |
IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION,
.vendor_id = OUI_TEAC,
.model_id = OUI_TEAC,
.specifier_id = OUI_TEAC,
.version = 0x800006,
.driver_data = (kernel_ulong_t)snd_dice_detect_teac_formats,
},
{ }
};
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);

View File

@ -233,5 +233,6 @@ int snd_dice_detect_presonus_formats(struct snd_dice *dice);
int snd_dice_detect_harman_formats(struct snd_dice *dice);
int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice);
int snd_dice_detect_weiss_formats(struct snd_dice *dice);
int snd_dice_detect_teac_formats(struct snd_dice *dice);
#endif

View File

@ -793,6 +793,7 @@ static const struct hda_device_id snd_hda_id_intelhdmi[] = {
HDA_CODEC_ID_MODEL(0x80862820, "Lunar Lake HDMI", MODEL_ADLP),
HDA_CODEC_ID_MODEL(0x80862822, "Panther Lake HDMI", MODEL_ADLP),
HDA_CODEC_ID_MODEL(0x80862823, "Wildcat Lake HDMI", MODEL_ADLP),
HDA_CODEC_ID_MODEL(0x80862824, "Nova Lake HDMI", MODEL_ADLP),
HDA_CODEC_ID_MODEL(0x80862882, "Valleyview2 HDMI", MODEL_BYT),
HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT),
{} /* terminator */

View File

@ -3406,7 +3406,42 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec,
spec->power_hook = alc287_s4_power_gpio3_default;
spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook;
}
/* GPIO2: mute led GPIO3: micmute led */
static void alc245_tas2781_spi_hp_fixup_muteled(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
static const hda_nid_t conn[] = { 0x02 };
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->gen.auto_mute_via_amp = 1;
snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
break;
}
tas2781_fixup_spi(codec, fix, action);
alc_fixup_hp_gpio_led(codec, action, 0x04, 0x0);
alc285_fixup_hp_coef_micmute_led(codec, fix, action);
}
/* JD2: mute led GPIO3: micmute led */
static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
static const hda_nid_t conn[] = { 0x02 };
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->gen.auto_mute_via_amp = 1;
snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
break;
}
tas2781_fixup_txnw_i2c(codec, fix, action);
alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
alc285_fixup_hp_coef_micmute_led(codec, fix, action);
}
/*
* Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly
* at PM resume
@ -3418,6 +3453,21 @@ static void alc283_fixup_dell_hp_resume(struct hda_codec *codec,
alc_write_coef_idx(codec, 0xd, 0x2800);
}
/* Swap DAC assignments for HP and speaker */
static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
static hda_nid_t preferred_pairs[] = {
0x21, 0x03, 0x14, 0x02, 0
};
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
spec->gen.preferred_dacs = preferred_pairs;
}
enum {
ALC269_FIXUP_GPIO2,
ALC269_FIXUP_SONY_VAIO,
@ -3737,6 +3787,10 @@ enum {
ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC,
ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK,
ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE,
ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED,
ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED,
ALC288_FIXUP_SURFACE_SWAP_DACS,
ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@ -5270,6 +5324,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc236_fixup_hp_mute_led_micmute_vref,
},
[ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc236_fixup_hp_mute_led_coefbit2,
.chained = true,
.chain_id = ALC236_FIXUP_HP_GPIO_LED,
},
[ALC236_FIXUP_LENOVO_INV_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic,
@ -6183,7 +6243,19 @@ static const struct hda_fixup alc269_fixups[] = {
},
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
}
},
[ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_tas2781_spi_hp_fixup_muteled,
},
[ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_tas2781_i2c_hp_fixup_muteled,
},
[ALC288_FIXUP_SURFACE_SWAP_DACS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc288_fixup_surface_swap_dacs,
},
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@ -6526,6 +6598,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a76, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a77, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
@ -6696,15 +6770,29 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8ed5, "HP Merino13X", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8ed6, "HP Merino13", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8ed7, "HP Merino14", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8ed8, "HP Merino16", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8ed9, "HP Merino14W", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8eda, "HP Merino16W", ALC245_FIXUP_TAS2781_SPI_2),
SND_PCI_QUIRK(0x103c, 0x8f40, "HP Lampas14", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x103c, 0x8f41, "HP Lampas16", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x103c, 0x8f42, "HP LampasW14", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8eb7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8eb8, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ec1, "HP 200 G2i", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ec4, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ec5, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ece, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ecf, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ed2, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ed5, "HP EliteBook 8 Flip G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8ed6, "HP EliteBook 8 G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8ed7, "HP EliteBook 8 G2i 14", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8ed8, "HP EliteBook 8 G2i 16", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8ed9, "HP ZBook Firefly 14W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8eda, "HP ZBook Firefly 16W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
@ -6736,6 +6824,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1384, "ASUS RC73XA", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x1043, 0x1394, "ASUS RC73YA", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC),
@ -6883,6 +6973,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
SND_PCI_QUIRK(0x1414, 0x9c20, "Microsoft Surface Pro 2/3", ALC288_FIXUP_SURFACE_SWAP_DACS),
SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP),

View File

@ -19,6 +19,9 @@
#include "hda_jack.h"
#include "generic.h"
/* GPIO node ID */
#define SENARY_GPIO_NODE 0x01
struct senary_spec {
struct hda_gen_spec gen;
@ -120,11 +123,11 @@ static void senary_init_gpio_led(struct hda_codec *codec)
unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
if (mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_MASK,
mask);
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DIRECTION,
mask);
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
}
}

View File

@ -88,6 +88,21 @@ config SND_HDA_SCODEC_CS35L56_SPI
Say Y or M here to include CS35L56 amplifier support with
SPI control.
menu "CS35L56 driver options"
depends on SND_HDA_SCODEC_CS35L56
config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS
bool "CS35L56 create debugfs for factory calibration"
default N
depends on DEBUG_FS
select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
help
Create debugfs entries used during factory-line manufacture
for factory calibration.
If unsure select "N".
endmenu
config SND_HDA_SCODEC_TAS2781
tristate
select SND_HDA_GENERIC

View File

@ -548,20 +548,24 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw
kfree(coeff_filename);
}
static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
{
int ret;
if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
return;
return -EACCES;
ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
&cs35l56_calibration_controls,
&cs35l56->base.cal_data);
if (ret < 0)
if (ret < 0) {
dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
else
dev_info(cs35l56->base.dev, "Calibration applied\n");
return ret;
}
dev_info(cs35l56->base.dev, "Calibration applied\n");
return 0;
}
static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
@ -669,7 +673,9 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (ret)
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
/* Don't need to check return code, it's not fatal if this fails */
cs35l56_hda_apply_calibration(cs35l56);
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret)
cs_dsp_stop(&cs35l56->cs_dsp);
@ -695,6 +701,100 @@ static void cs35l56_hda_dsp_work(struct work_struct *work)
cs35l56_hda_fw_load(cs35l56);
}
static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file,
const char __user *from,
size_t count, loff_t *ppos)
{
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
ret = pm_runtime_resume_and_get(cs35l56_base->dev);
if (ret)
return ret;
ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
pm_runtime_autosuspend(cs35l56_base->dev);
return ret;
}
static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
const char __user *from,
size_t count, loff_t *ppos)
{
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
ret = pm_runtime_resume_and_get(cs35l56_base->dev);
if (ret)
return ret;
ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
pm_runtime_autosuspend(cs35l56_base->dev);
return ret;
}
static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
char __user *to,
size_t count, loff_t *ppos)
{
struct cs35l56_base *cs35l56_base = file->private_data;
ssize_t ret;
ret = pm_runtime_resume_and_get(cs35l56_base->dev);
if (ret)
return ret;
ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
pm_runtime_autosuspend(cs35l56_base->dev);
return ret;
}
static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
const char __user *from,
size_t count, loff_t *ppos)
{
struct cs35l56_base *cs35l56_base = file->private_data;
struct cs35l56_hda *cs35l56 = cs35l56_hda_from_base(cs35l56_base);
ssize_t ret;
ret = cs35l56_cal_data_debugfs_write(cs35l56_base, from, count, ppos);
if (ret == -ENODATA)
return count; /* Ignore writes of empty cal blobs */
if (ret < 0)
return ret;
ret = pm_runtime_resume_and_get(cs35l56_base->dev);
if (ret)
return ret;
ret = cs35l56_hda_apply_calibration(cs35l56);
if (ret == 0)
cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_REINIT);
else
count = -EIO;
pm_runtime_autosuspend(cs35l56_base->dev);
return count;
}
static const struct cs35l56_cal_debugfs_fops cs35l56_hda_cal_debugfs_fops = {
.calibrate = {
.write = cs35l56_hda_debugfs_calibrate_write,
},
.cal_temperature = {
.write = cs35l56_hda_debugfs_cal_temperature_write,
},
.cal_data = {
.read = cs35l56_hda_debugfs_cal_data_read,
.write = cs35l56_hda_debugfs_cal_data_write,
},
};
static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
@ -722,6 +822,9 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas
cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
#endif
if (IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS))
cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_hda_cal_debugfs_fops);
dev_dbg(cs35l56->base.dev, "Bound\n");
return 0;
@ -735,6 +838,7 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
cancel_work_sync(&cs35l56->dsp_work);
cs35l56_remove_cal_debugfs(&cs35l56->base);
cs35l56_hda_remove_controls(cs35l56);
#if IS_ENABLED(CONFIG_SND_DEBUG)
@ -1050,7 +1154,7 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
}
cs35l56->base.type = hid & 0xff;
cs35l56->base.cal_index = -1;
cs35l56->base.cal_index = cs35l56->index;
cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;

View File

@ -9,6 +9,7 @@
#ifndef __CS35L56_HDA_H__
#define __CS35L56_HDA_H__
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/firmware/cirrus/cs_dsp.h>
@ -42,6 +43,11 @@ struct cs35l56_hda {
#endif
};
static inline struct cs35l56_hda *cs35l56_hda_from_base(struct cs35l56_base *cs35l56_base)
{
return container_of(cs35l56_base, struct cs35l56_hda, base);
}
extern const struct dev_pm_ops cs35l56_hda_pm_ops;
int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);

View File

@ -87,6 +87,7 @@ static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = {
static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
{
struct gpio_desc *speaker_id;
struct acpi_device *adev;
struct device *physdev;
LIST_HEAD(resources);
@ -119,19 +120,31 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
/* Speaker id was needed for ASUS projects. */
ret = kstrtou32(sub, 16, &subid);
if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) {
ret = devm_acpi_dev_add_driver_gpios(p->dev,
tas2781_speaker_id_gpios);
if (ret < 0)
ret = acpi_dev_add_driver_gpios(adev, tas2781_speaker_id_gpios);
if (ret < 0) {
dev_err(p->dev, "Failed to add driver gpio %d.\n",
ret);
p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN);
if (IS_ERR(p->speaker_id)) {
dev_err(p->dev, "Failed to get Speaker id.\n");
ret = PTR_ERR(p->speaker_id);
goto err;
p->speaker_id = -1;
goto end_2563;
}
speaker_id = fwnode_gpiod_get_index(acpi_fwnode_handle(adev),
"speakerid", 0, GPIOD_IN, NULL);
if (!IS_ERR(speaker_id)) {
p->speaker_id = gpiod_get_value_cansleep(speaker_id);
dev_dbg(p->dev, "Got speaker id gpio from ACPI: %d.\n",
p->speaker_id);
gpiod_put(speaker_id);
} else {
p->speaker_id = -1;
ret = PTR_ERR(speaker_id);
dev_err(p->dev, "Get speaker id gpio failed %d.\n",
ret);
}
acpi_dev_remove_driver_gpios(adev);
} else {
p->speaker_id = NULL;
p->speaker_id = -1;
}
end_2563:
@ -432,23 +445,16 @@ static void tasdevice_dspfw_init(void *context)
struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
struct hda_codec *codec = tas_priv->codec;
int ret, spk_id;
int ret;
tasdevice_dsp_remove(tas_priv);
tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
if (tas_priv->speaker_id != NULL) {
// Speaker id need to be checked for ASUS only.
spk_id = gpiod_get_value(tas_priv->speaker_id);
if (spk_id < 0) {
// Speaker id is not valid, use default.
dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id);
spk_id = 0;
}
if (tas_priv->speaker_id >= 0) {
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),
"TAS2XXX%04X%d.bin",
lower_16_bits(codec->core.subsystem_id),
spk_id);
tas_priv->speaker_id);
} else {
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),

Some files were not shown because too many files have changed in this diff Show More