* [PATCH v3 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
@ 2026-10-18 5:53 ` James Calligeros
2026-10-18 5:53 ` [PATCH v3 02/21] ASoC: dt-bindings: cs42l42: Document additional dai-common properties James Calligeros
` (19 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:53 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
Some consumers may wish to prepend the codec's name with a known value
to identify a specific codec among many. Enable this with the common
sound-name-prefix property.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../bindings/sound/cirrus,cs42l84.yaml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
index 94062c1d9d45..ca137aa57461 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
@@ -9,6 +9,9 @@ title: Cirrus Logic CS42L84 audio CODEC
maintainers:
- Martin Povišer <povik+lin@cutebit.org>
+allOf:
+ - $ref: dai-common.yaml#
+
description: |
The CS42L84 is a headphone jack codec made by Cirrus Logic and embedded
in personal computers sold by Apple. It was first seen in 2021 Macbook
@@ -32,6 +35,8 @@ properties:
'#sound-dai-cells':
const: 0
+ sound-name-prefix: true
+
required:
- compatible
- reg
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 02/21] ASoC: dt-bindings: cs42l42: Document additional dai-common properties
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
2026-10-18 5:53 ` [PATCH v3 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
@ 2026-10-18 5:53 ` James Calligeros
2026-10-18 6:04 ` sashiko-bot
2026-10-18 5:53 ` [PATCH v3 03/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
` (18 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:53 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
We may need to define both #sound-dai-cells and sound-name-prefix on some
machines where this codec is part of a simple DAI. Document them with
reference to dai-common to keep the style checker happy.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../bindings/sound/cirrus,cs42l42.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
index ab4c7d22dc0e..aba2121ac64a 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
@@ -9,6 +9,9 @@ title: Cirrus Logic CS42L42 audio CODEC
maintainers:
- patches@opensource.cirrus.com
+allOf:
+ - $ref: dai-common.yaml#
+
description:
The CS42L42 is a low-power audio codec designed for portable applications.
It provides a high-dynamic range, stereo DAC for audio playback and a mono
@@ -26,6 +29,10 @@ properties:
The I2C address of the CS42L42.
maxItems: 1
+ '#sound-dai-cells': true
+
+ sound-name-prefix: true
+
VP-supply:
description:
VP power supply.
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 02/21] ASoC: dt-bindings: cs42l42: Document additional dai-common properties
2026-10-18 5:53 ` [PATCH v3 02/21] ASoC: dt-bindings: cs42l42: Document additional dai-common properties James Calligeros
@ 2026-10-18 6:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:04 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] ASoC: dt-bindings: cs42l42: unconstrained #sound-dai-cells property
--
commit 41e0b44d1b55811c2f6af66b07ffb649188a35d3
Author: James Calligeros <jcalligeros99@gmail.com>
ASoC: dt-bindings: cs42l42: Document additional dai-common properties
This patch references dai-common.yaml# and documents #sound-dai-cells
and sound-name-prefix in the cirrus,cs42l42.yaml device tree binding.
> diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
> index ab4c7d22dc0eb..aba2121ac64a7 100644
> --- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
> +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
> @@ -9,6 +9,9 @@ title: Cirrus Logic CS42L42 audio CODEC
> maintainers:
> - patches@opensource.cirrus.com
>
> +allOf:
> + - $ref: dai-common.yaml#
> +
> description:
> The CS42L42 is a low-power audio codec designed for portable applications.
> It provides a high-dynamic range, stereo DAC for audio playback and a mono
> @@ -26,6 +29,10 @@ properties:
> The I2C address of the CS42L42.
> maxItems: 1
>
> + '#sound-dai-cells': true
> +
> + sound-name-prefix: true
> +
[Severity: Low]
Should '#sound-dai-cells' be constrained to const: 0 here instead of true?
In Documentation/devicetree/bindings/sound/dai-common.yaml,
'#sound-dai-cells': true is a generic placeholder, and referencing codec
schemas normally constrain the cell count to match the hardware.
In sound/soc/codecs/cs42l42.c, cs42l42_common_probe() registers a single
DAI without a custom .of_xlate_dai_name callback:
ret = devm_snd_soc_register_component(cs42l42->dev, component_drv, dai, 1);
Leaving '#sound-dai-cells' as true allows non-zero cell counts in device
tree nodes to pass schema validation while failing at runtime during
ASoC DAI phandle resolution.
Also, if additionalProperties: false at the bottom of
Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml is changed
to unevaluatedProperties: false when referencing dai-common.yaml# in
allOf, would that avoid needing to list sound-name-prefix: true here?
> VP-supply:
> description:
> VP power supply.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=2
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 03/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
2026-10-18 5:53 ` [PATCH v3 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
2026-10-18 5:53 ` [PATCH v3 02/21] ASoC: dt-bindings: cs42l42: Document additional dai-common properties James Calligeros
@ 2026-10-18 5:53 ` James Calligeros
2026-10-18 6:07 ` sashiko-bot
2026-10-18 5:53 ` [PATCH v3 04/21] ASoC: ops: Introduce 'snd_soc_ctl_activate' James Calligeros
` (17 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:53 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
From: Martin Povišer <povik+lin@cutebit.org>
Add a binding for the primary audio subsystem found on Apple Silicon
Macs. This binding integrates the codecs and SoC I2S peripheral
used for driving the headset jack and speaker arrays on all Apple
Silicon Macs. The internal microphones on this platform are
connected to a distinct coprocessor and are not part of this
"main" audio subsystem.
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Co-developed-by: James Calligeros <jcalligeros99@gmail.com>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../bindings/sound/apple,macaudio.yaml | 212 +++++++++++++++++++++++++
1 file changed, 212 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml
new file mode 100644
index 000000000000..af8e9686ba7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml
@@ -0,0 +1,212 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/apple,macaudio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sound peripheral integration for Apple Silicon Macs
+
+description:
+ The speakers and headphone jack on Apple Silicon Macs are
+ part of an integrated audio subsystem comprising of Apple's
+ I2S peripheral (MCA) and a series of codecs. The machines
+ have a wide variety of topologies ranging from a simple
+ mono speaker in the desktops to complex stereo arrays with
+ dual-opposed woofers and tweeters all driven by their own
+ mono amplifiers. This arrangement can be expressed as a
+ set of DAI links, where the MCA ports each codec is connected
+ to are represented by the CPU side of the link.
+
+maintainers:
+ - James Calligeros <jcalligeros99@gmail.com>
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - apple,j180-macaudio
+ - apple,j274-macaudio
+ - apple,j293-macaudio
+ - apple,j313-macaudio
+ - apple,j314-macaudio
+ - apple,j316-macaudio
+ - apple,j375-macaudio
+ - apple,j413-macaudio
+ - apple,j415-macaudio
+ - apple,j456-macaudio
+ - apple,j473-macaudio
+ - items:
+ - enum:
+ - apple,j493-macaudio
+ - const: apple,j293-macaudio
+ - items:
+ - enum:
+ - apple,j414-macaudio
+ - apple,j504-macaudio
+ - apple,j514-macaudio
+ - const: apple,j314-macaudio
+ - items:
+ - enum:
+ - apple,j416-macaudio
+ - apple,j516-macaudio
+ - const: apple,j316-macaudio
+ - items:
+ - enum:
+ - apple,j475-macaudio
+ - apple,j575-macaudio
+ - const: apple,j375-macaudio
+ - items:
+ - enum:
+ - apple,j613-macaudio
+ - const: apple,j413-macaudio
+ - items:
+ - enum:
+ - apple,j615-macaudio
+ - const: apple,j415-macaudio
+ - items:
+ - enum:
+ - apple,j457-macaudio
+ - const: apple,j456-macaudio
+ - items:
+ - enum:
+ - apple,j474-macaudio
+ - const: apple,j473-macaudio
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ model:
+ description:
+ Model name for presentation to users
+ $ref: /schemas/types.yaml#/definitions/string
+
+patternProperties:
+ "^dai-link(@[0-9a-f]+)?$":
+ description: |
+ Node for each sound peripheral such as the speaker array, headphones jack,
+ or microphone.
+ type: object
+ $ref: tdm-slot.yaml#
+ additionalProperties: false
+
+ properties:
+ reg:
+ maxItems: 1
+
+ link-name:
+ description: Name for the peripheral
+ enum: [ Headphone Jack, Speaker, Speakers ]
+ $ref: /schemas/types.yaml#/definitions/string
+
+ codec:
+ type: object
+ additionalProperties: false
+
+ properties:
+ sound-dai:
+ minItems: 1
+ maxItems: 8
+ description: |
+ DAI list with the CODEC-side DAIs connected to the above CPU-side
+ DAIs and involved in this sound peripheral.
+
+ The list is in left/right order if applicable. If there are more
+ than one CPU-side DAIs (there can be two), the CODECs must be
+ listed first those connected to the first CPU, then those
+ connected to the second.
+
+ In addition, on some machines with many speaker codecs, the CODECs
+ are listed in this fixed order:
+
+ Four speakers: Left Front, Left Rear, Right Front, Right Rear
+ Six speakers: Left Woofer 1, Left Tweeter, Left Woofer 2,
+ Right Woofer 1, Right Tweeter, Right Woofer 2
+
+ required:
+ - sound-dai
+
+ cpu:
+ type: object
+ additionalProperties: false
+
+ properties:
+ sound-dai:
+ minItems: 1
+ maxItems: 2
+ description: |
+ DAI list with CPU-side I2S ports involved in this peripheral.
+
+ required:
+ - sound-dai
+
+ patternProperties:
+ "^dai-tdm-idle-mode-[0-9]$":
+ $ref: tdm-slot.yaml#/properties/dai-tdm-idle-mode
+
+ "^dai-tdm-slot-tx-idle-mask-[0-9]$":
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Idle slot mask. A bit being set to 1 indicates
+ that the corresponding TDM slot is inactive/idle.
+
+ required:
+ - reg
+ - codec
+ - cpu
+
+required:
+ - '#address-cells'
+ - '#size-cells'
+ - compatible
+ - model
+
+additionalProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "apple,j416-macaudio", "apple,j316-macaudio";
+ model = "MacBook Pro J416";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+ link-name = "Speakers";
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-3 = "zero";
+ dai-tdm-slot-tx-idle-mask-0 = <0xf0f0f0>;
+ dai-tdm-slot-tx-idle-mask-3 = <0x0f0f0f>;
+
+ codec {
+ sound-dai = <&speaker_left_woof1>,
+ <&speaker_left_tweet>,
+ <&speaker_left_woof2>,
+ <&speaker_right_woof1>,
+ <&speaker_right_tweet>,
+ <&speaker_right_woof2>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 03/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio
2026-10-18 5:53 ` [PATCH v3 03/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
@ 2026-10-18 6:07 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:07 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] ASoC: dt-bindings: apple,macaudio: additionalProperties blocks tdm-slot $ref
--
commit 212ef47e302889aa8e011388085ecf8d026993bf
Author: Martin Povišer <povik+lin@cutebit.org>
ASoC: dt-bindings: Add binding for Apple Silicon Mac audio
This commit adds a devicetree binding for the primary audio subsystem on
Apple Silicon Macs, integrating the MCA I2S peripheral and codecs for
driving the headphone jack and speaker arrays.
> diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml
> new file mode 100644
> index 0000000000000..af8e9686ba7bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml
> @@ -0,0 +1,212 @@
[ ... ]
> +patternProperties:
> + "^dai-link(@[0-9a-f]+)?$":
> + description: |
> + Node for each sound peripheral such as the speaker array, headphones jack,
> + or microphone.
> + type: object
> + $ref: tdm-slot.yaml#
> + additionalProperties: false
[Severity: Low]
Does setting additionalProperties: false alongside $ref: tdm-slot.yaml# in
the ^dai-link(@[0-9a-f]+)?$ schema block all properties defined in
tdm-slot.yaml?
In dtschema, additionalProperties: false only permits property names
matched by the immediate schema object's properties and patternProperties
keywords, ignoring properties defined in a sibling $ref unless they are
also listed locally:
Documentation/devicetree/bindings/writing-schema.rst:
* additionalProperties: false
Most common case, where no additional schema is referenced or if this
binding allows subset of properties from other referenced schemas.
* unevaluatedProperties: false
Used when this binding references other schema whose all properties
should be allowed.
Because none of the properties from tdm-slot.yaml (such as
dai-tdm-slot-num, dai-tdm-slot-width, dai-tdm-idle-mode, or the mask
properties) are listed in the local properties or patternProperties of
^dai-link(@[0-9a-f]+)?$, any dai-link subnode using standard tdm-slot.yaml
properties will fail dtbs_check validation.
Should this use unevaluatedProperties: false instead, or should the
node-level $ref: tdm-slot.yaml# be dropped if only the locally defined
^dai-tdm-idle-mode-[0-9]$ and ^dai-tdm-slot-tx-idle-mask-[0-9]$
patternProperties are intended?
[ ... ]
> + patternProperties:
> + "^dai-tdm-idle-mode-[0-9]$":
> + $ref: tdm-slot.yaml#/properties/dai-tdm-idle-mode
> +
> + "^dai-tdm-slot-tx-idle-mask-[0-9]$":
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: Idle slot mask. A bit being set to 1 indicates
> + that the corresponding TDM slot is inactive/idle.
> +
> + required:
> + - reg
> + - codec
> + - cpu
[ ... ]
>
> --
> 2.51.2
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=3
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 04/21] ASoC: ops: Introduce 'snd_soc_ctl_activate'
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (2 preceding siblings ...)
2026-10-18 5:53 ` [PATCH v3 03/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
@ 2026-10-18 5:53 ` James Calligeros
2026-10-18 5:53 ` [PATCH v3 05/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
` (16 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:53 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
From: Martin Povišer <povik+lin@cutebit.org>
This function mirrors wraps the sound/core 'snd_ctl_activate_id'
for ASoC. Machine drivers may call it to lock down kcontrols
during fixup_controls.
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
include/sound/soc.h | 2 ++
sound/soc/soc-ops.c | 32 +++++++++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 553bb7e2be98..7509824a6b2e 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -578,6 +578,8 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max);
+int snd_soc_ctl_activate(struct snd_soc_card *card,
+ const char *name, int active);
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 142f407e3939..295056bcc081 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -471,6 +471,38 @@ int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max)
}
EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+/**
+ * snd_soc_ctl_activate - Activate/deactivate a control matching a name
+ *
+ * @card: where to look for the control
+ * @name: name
+ * @active: zero to deactivate, nonzero to activate
+ *
+ * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_soc_ctl_activate(struct snd_soc_card *card,
+ const char *name, int active)
+{
+ struct snd_kcontrol *kctl;
+ int ret = -EINVAL;
+
+ if (!name)
+ return ret;
+
+ kctl = snd_soc_card_get_kcontrol(card, name);
+
+ if (kctl) {
+ ret = snd_ctl_activate_id(card->snd_card, &kctl->id, active);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_dbg(card->dev, "Tried to de/activate unknown control '%s'\n", name);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_ctl_activate);
+
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 05/21] ASoC: ops: Introduce 'soc_set_enum_kctl'
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (3 preceding siblings ...)
2026-10-18 5:53 ` [PATCH v3 04/21] ASoC: ops: Introduce 'snd_soc_ctl_activate' James Calligeros
@ 2026-10-18 5:53 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 06/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
` (15 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:53 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
From: Martin Povišer <povik+lin@cutebit.org>
The new function is to be used to set enumerated controls to desired
values. It is something a machine driver may call in fixup_controls.
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
include/sound/soc.h | 2 +
sound/soc/soc-ops.c | 67 +++++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 7509824a6b2e..dcfd706a73ad 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -580,6 +580,8 @@ int snd_soc_limit_volume(struct snd_soc_card *card,
const char *name, int max);
int snd_soc_ctl_activate(struct snd_soc_card *card,
const char *name, int active);
+int snd_soc_set_enum_kctl(struct snd_soc_card *card,
+ const char *name, const char *strval);
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 295056bcc081..149a094112d2 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -503,6 +503,73 @@ int snd_soc_ctl_activate(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_ctl_activate);
+static int soc_set_enum_kctl(struct snd_kcontrol *kctl, const char *value)
+{
+ int sel, i, ret;
+
+ struct snd_ctl_elem_value *evalue __free(kfree) = kzalloc_obj(*evalue);
+ if (!evalue)
+ return -ENOMEM;
+
+ struct snd_ctl_elem_info *info __free(kfree) = kzalloc_obj(*info);
+ if (!info)
+ return -ENOMEM;
+
+ info->id = evalue->id = kctl->id;
+
+ ret = kctl->info(kctl, info);
+ if (ret < 0)
+ return ret;
+
+ if (!kctl->put)
+ return -EINVAL;
+
+ if (info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED)
+ return -EINVAL;
+
+ for (sel = 0; sel < info->value.enumerated.items; sel++) {
+ info->value.enumerated.item = sel;
+ ret = kctl->info(kctl, info);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(value, info->value.enumerated.name))
+ break;
+ }
+
+ if (sel == info->value.enumerated.items)
+ return -ENOENT;
+
+ for (i = 0; i < info->count; i++)
+ evalue->value.enumerated.item[i] = sel;
+
+ return kctl->put(kctl, evalue);
+}
+
+/**
+ * snd_soc_set_enum_kctl - Set enumerated control matching a name
+ *
+ * @card: where to look for the controls
+ * @name: name
+ * @value: string value to set the controls to
+ *
+ * Return 0 on success, else error.
+ */
+int snd_soc_set_enum_kctl(struct snd_soc_card *card,
+ const char *name, const char *value)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_soc_card_get_kcontrol(card, name);
+ if (!kctl) {
+ dev_dbg(card->dev, "Tried to set unknown control '%s' to '%s'\n", name, value);
+ return -EINVAL;
+ }
+
+ return soc_set_enum_kctl(kctl, value);
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_enum_kctl);
+
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 06/21] ASoC: card: Let 'fixup_controls' return errors
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (4 preceding siblings ...)
2026-10-18 5:53 ` [PATCH v3 05/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 07/21] ASoC: tas2770: Add compatible for TAS5770L James Calligeros
` (14 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
From: Martin Povišer <povik+lin@cutebit.org>
Let the 'fixup_controls' card method return error values which will roll
back the half-done binding of the card.
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
include/sound/soc-card.h | 2 +-
include/sound/soc.h | 2 +-
.../soc/mediatek/mt8188/mt8188-mt6359.c | 4 +++-
sound/soc/soc-card.c | 12 +++++++++---
sound/soc/soc-core.c | 5 ++++-
5 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/include/sound/soc-card.h b/include/sound/soc-card.h
index 1f1e7d45f553..c392e6c4844b 100644
--- a/include/sound/soc-card.h
+++ b/include/sound/soc-card.h
@@ -44,7 +44,7 @@ int snd_soc_card_resume_post(struct snd_soc_card *card);
int snd_soc_card_probe(struct snd_soc_card *card);
int snd_soc_card_late_probe(struct snd_soc_card *card);
-void snd_soc_card_fixup_controls(struct snd_soc_card *card);
+int snd_soc_card_fixup_controls(struct snd_soc_card *card);
int snd_soc_card_remove(struct snd_soc_card *card);
void snd_soc_card_set_topology_name(struct snd_soc_card *card, const char *preifx);
diff --git a/include/sound/soc.h b/include/sound/soc.h
index dcfd706a73ad..a3b9d2610514 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1005,7 +1005,7 @@ struct snd_soc_card {
int (*probe)(struct snd_soc_card *card);
int (*late_probe)(struct snd_soc_card *card);
- void (*fixup_controls)(struct snd_soc_card *card);
+ int (*fixup_controls)(struct snd_soc_card *card);
int (*remove)(struct snd_soc_card *card);
/* the pre and post PM functions are used to do any PM work before and
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 75c90d1d165f..14890af97706 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -1280,7 +1280,7 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
},
};
-static void mt8188_fixup_controls(struct snd_soc_card *card)
+static int mt8188_fixup_controls(struct snd_soc_card *card)
{
struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
struct mtk_platform_card_data *card_data = soc_card_data->card_data;
@@ -1302,6 +1302,8 @@ static void mt8188_fixup_controls(struct snd_soc_card *card)
else
dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n");
}
+
+ return 0;
}
static struct snd_soc_card mt8188_mt6359_soc_card = {
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
index 282d666dae9e..e4d81329cde3 100644
--- a/sound/soc/soc-card.c
+++ b/sound/soc/soc-card.c
@@ -184,10 +184,16 @@ int snd_soc_card_late_probe(struct snd_soc_card *card)
return 0;
}
-void snd_soc_card_fixup_controls(struct snd_soc_card *card)
+int snd_soc_card_fixup_controls(struct snd_soc_card *card)
{
- if (card->fixup_controls)
- card->fixup_controls(card);
+ if (card->fixup_controls) {
+ int ret = card->fixup_controls(card);
+
+ if (ret < 0)
+ return soc_card_ret(card, ret);
+ }
+
+ return 0;
}
int snd_soc_card_remove(struct snd_soc_card *card)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 06167a198591..2092373698db 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2109,7 +2109,10 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
if (ret < 0)
goto probe_end;
}
- snd_soc_card_fixup_controls(card);
+
+ ret = snd_soc_card_fixup_controls(card);
+ if (ret < 0)
+ goto probe_end;
ret = snd_card_register(card->snd_card);
if (ret < 0) {
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 07/21] ASoC: tas2770: Add compatible for TAS5770L
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (5 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 06/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 08/21] ASoC: apple: Add macaudio machine driver James Calligeros
` (13 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
TAS5770L is an Apple-specific variant of TAS2770 found in a number
of first-generation Apple Silicon Macs. Add a compatible for it.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
sound/soc/codecs/tas2770.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index 882e19857697..5dfddd3bd049 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -978,6 +978,7 @@ MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
#if defined(CONFIG_OF)
static const struct of_device_id tas2770_of_match[] = {
{ .compatible = "ti,tas2770" },
+ { .compatible = "ti,tas5770l" }, /* Apple variant */
{},
};
MODULE_DEVICE_TABLE(of, tas2770_of_match);
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 08/21] ASoC: apple: Add macaudio machine driver
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (6 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 07/21] ASoC: tas2770: Add compatible for TAS5770L James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 6:23 ` sashiko-bot
2026-10-18 5:54 ` [PATCH v3 09/21] arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply James Calligeros
` (12 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, Hector Martin,
Sasha Finkelstein
From: Martin Povišer <povik+lin@cutebit.org>
Apple Silicon Macs have a complex audio subsystem consisting
of an I2S peripheral (MCA) and multiple codecs of various
models and capabilities. Some machines have a basic mono
speaker with hardware downmix, while others have a very
intricate stereo system consisting of multiple codecs and
drivers per L/R channel. Some machines report voice coil
voltage and current information back to the SoC, and others
do not. All machines have a headset jack.
Add an ASoC machine driver for this platform.
To express all of this sanely, we initialise the correct
combinations of codecs/I2S ports as DAI backends and link
them to a synthetic frontend. Each frontend is linked to
a PCM, one for the headset codec and one for the speaker
system on the target machine.
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Co-developed-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Janne Grunau <j@jannau.net>
Signed-off-by: Janne Grunau <j@jannau.net>
Co-developed-by: Sasha Finkelstein <fnkl.kernel@gmail.com>
Signed-off-by: Sasha Finkelstein <fnkl.kernel@gmail.com>
Co-developed-by: James Calligeros <jcalligeros99@gmail.com>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
sound/soc/apple/Kconfig | 18 +
sound/soc/apple/Makefile | 4 +
sound/soc/apple/macaudio.c | 1715 +++++++++++++++++++++++++
3 files changed, 1737 insertions(+)
diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig
index f1d071a99fbf..549a5f4906c5 100644
--- a/sound/soc/apple/Kconfig
+++ b/sound/soc/apple/Kconfig
@@ -9,4 +9,22 @@ config SND_SOC_APPLE_MCA
This option enables an ASoC platform driver for MCA peripherals found
on Apple Silicon SoCs.
+config SND_SOC_APPLE_MACAUDIO
+ tristate "Sound support for Apple Silicon Macs"
+ depends on ARCH_APPLE || COMPILE_TEST
+ select SND_SOC_APPLE_MCA
+ select APPLE_ADMAC if DMADEVICES
+ select COMMON_CLK_APPLE_NCO if COMMON_CLK
+ select SND_SOC_TAS2764 if I2C
+ select SND_SOC_TAS2770 if I2C
+ select SND_SOC_CS42L83 if I2C
+ select SND_SOC_CS42L84 if I2C
+ help
+ This option enables an ASoC machine-level driver for Apple Silicon Macs
+ and it also enables the required SoC and codec drivers for overall
+ sound support on these machines. Say Y here if you have an Apple Silicon
+ Mac.
+
+ This driver can also be compiled as a module. If so, it will be called
+ snd-soc-macaudio.
endmenu
diff --git a/sound/soc/apple/Makefile b/sound/soc/apple/Makefile
index 252ba5fc18cb..90ad83adc588 100644
--- a/sound/soc/apple/Makefile
+++ b/sound/soc/apple/Makefile
@@ -2,3 +2,7 @@
snd-soc-apple-mca-y := mca.o
obj-$(CONFIG_SND_SOC_APPLE_MCA) += snd-soc-apple-mca.o
+
+snd-soc-macaudio-y := macaudio.o
+
+obj-$(CONFIG_SND_SOC_APPLE_MACAUDIO) += snd-soc-macaudio.o
diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c
new file mode 100644
index 000000000000..b8e58c9a676f
--- /dev/null
+++ b/sound/soc/apple/macaudio.c
@@ -0,0 +1,1715 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ASoC machine driver for Apple Silicon Macs
+//
+// Copyright (C) The Asahi Linux Contributors
+//
+// Based on sound/soc/qcom/{sc7180.c|common.c}
+// Copyright (c) 2018, Linaro Limited.
+// Copyright (c) 2020, The Linux Foundation. All rights reserved.
+//
+//
+// The platform driver has independent frontend and backend DAIs with the
+// option of routing backends to any of the frontends. The platform
+// driver configures the routing based on DPCM couplings in ASoC runtime
+// structures, which in turn are determined from DAPM paths by ASoC. But the
+// platform driver doesn't supply relevant DAPM paths and leaves that up for
+// the machine driver to fill in. The filled-in virtual topology can be
+// anything as long as any backend isn't connected to more than one frontend
+// at any given time. (The limitation is due to the unsupported case of
+// reparenting of live BEs.)
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/simple_card_utils.h>
+#include <sound/soc.h>
+#include <sound/soc-jack.h>
+#include <uapi/linux/input-event-codes.h>
+
+#define DRIVER_NAME "snd-soc-macaudio"
+
+/*
+ * CPU side is bit and frame clock provider
+ * I2S has both clocks inverted
+ */
+#define MACAUDIO_DAI_FMT (SND_SOC_DAIFMT_I2S | \
+ SND_SOC_DAIFMT_CBC_CFC | \
+ SND_SOC_DAIFMT_GATED | \
+ SND_SOC_DAIFMT_IB_IF)
+#define MACAUDIO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_HEADPHONE)
+#define MACAUDIO_SLOTWIDTH 32
+/*
+ * Maximum BCLK frequency
+ *
+ * Codec maximums:
+ * CS42L42 26.0 MHz
+ * TAS2770 27.1 MHz
+ * TAS2764 24.576 MHz
+ */
+#define MACAUDIO_MAX_BCLK_FREQ 24576000
+
+#define SPEAKER_MAGIC_VALUE ((s32)0xdec1be15)
+/* milliseconds */
+#define SPEAKER_LOCK_TIMEOUT 250
+
+enum macaudio_amp_type {
+ AMP_NONE,
+ AMP_TAS5770,
+ AMP_SN012776,
+ AMP_SSM3515,
+};
+
+enum macaudio_spkr_config {
+ SPKR_NONE, /* No speakers */
+ SPKR_1W, /* 1 woofer / ch */
+ SPKR_2W, /* 2 woofers / ch */
+ SPKR_1W1T, /* 1 woofer + 1 tweeter / ch */
+ SPKR_2W1T, /* 2 woofers + 1 tweeter / ch */
+};
+
+struct macaudio_platform_cfg {
+ bool enable_speakers;
+ enum macaudio_amp_type amp;
+ enum macaudio_spkr_config speakers;
+ bool stereo;
+ int amp_gain;
+ int safe_vol;
+};
+
+static const char * const volume_control_names[] = {
+ [AMP_TAS5770] = "Speaker Playback Volume",
+ [AMP_SN012776] = "Speaker Volume",
+ [AMP_SSM3515] = "DAC Playback Volume",
+};
+
+#define SN012776_0DB 201
+#define SN012776_DB(x) (SN012776_0DB + 2 * (x))
+/* Same as SN012776 */
+#define TAS5770_0DB SN012776_0DB
+#define TAS5770_DB(x) SN012776_DB(x)
+
+#define SSM3515_0DB (255 - 64) /* +24dB max, steps of 3/8 dB */
+#define SSM3515_DB(x) (SSM3515_0DB + (8 * (x) / 3))
+
+struct ma_codec_idle {
+ int idle_mode;
+ u32 idle_mask;
+};
+
+static struct snd_soc_jack macaudio_jack;
+
+struct macaudio_snd_data {
+ struct snd_soc_card *card;
+ int jack_plugin_state;
+
+ const struct macaudio_platform_cfg *cfg;
+ bool has_speakers;
+ bool has_sense;
+ bool has_safety;
+ unsigned int max_channels;
+
+ struct macaudio_link_props {
+ /* frontend props */
+ unsigned int bclk_ratio;
+ bool is_sense;
+
+ /* backend props */
+ bool is_speakers;
+ bool is_headphones;
+ unsigned int tdm_mask;
+ struct ma_codec_idle *codecs;
+ } *link_props;
+
+ int speaker_sample_rate;
+ struct snd_kcontrol *speaker_sample_rate_kctl;
+
+ struct mutex be_link_mutex;
+
+ struct mutex volume_lock_mutex;
+ bool speaker_volume_unlocked;
+ bool speaker_volume_was_locked;
+ struct snd_kcontrol *speaker_lock_kctl;
+ u64 bes_active;
+ bool speaker_lock_timeout_enabled;
+ ktime_t speaker_lock_timeout;
+ ktime_t speaker_lock_remain;
+ struct delayed_work lock_timeout_work;
+ struct work_struct lock_update_work;
+
+};
+
+SND_SOC_DAILINK_DEFS(primary,
+ DAILINK_COMP_ARRAY(COMP_CPU("mca-pcm-0")), // CPU
+ DAILINK_COMP_ARRAY(COMP_DUMMY()), // CODEC
+ DAILINK_COMP_ARRAY(COMP_EMPTY())); // platform (filled at runtime)
+
+SND_SOC_DAILINK_DEFS(secondary,
+ DAILINK_COMP_ARRAY(COMP_CPU("mca-pcm-1")), // CPU
+ DAILINK_COMP_ARRAY(COMP_DUMMY()), // CODEC
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(sense,
+ DAILINK_COMP_ARRAY(COMP_CPU("mca-pcm-2")), // CPU
+ DAILINK_COMP_ARRAY(COMP_DUMMY()), // CODEC
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link macaudio_fe_links[] = {
+ {
+ .name = "Primary",
+ .stream_name = "Primary",
+ .dynamic = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_format = 1,
+ .dai_fmt = MACAUDIO_DAI_FMT,
+ SND_SOC_DAILINK_REG(primary),
+ },
+ {
+ .name = "Secondary",
+ .stream_name = "Secondary",
+ .dynamic = 1,
+ .dpcm_merged_rate = 1,
+ .dpcm_merged_chan = 1,
+ .dpcm_merged_format = 1,
+ .dai_fmt = MACAUDIO_DAI_FMT,
+ .playback_only = 1,
+ .nonatomic = 1,
+ SND_SOC_DAILINK_REG(secondary),
+ },
+ {
+ .name = "Speaker Sense",
+ .stream_name = "Speaker Sense",
+ .capture_only = 1,
+ .dynamic = 1,
+ .dai_fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_CBP_CFP |
+ SND_SOC_DAIFMT_GATED |
+ SND_SOC_DAIFMT_IB_IF),
+ .nonatomic = 1,
+ SND_SOC_DAILINK_REG(sense),
+ },
+};
+
+static struct macaudio_link_props macaudio_fe_link_props[] = {
+ {
+ /*
+ * Primary FE
+ *
+ * The bclk ratio at 64 for the primary frontend is important
+ * to ensure that the headphones codec's idea of left and right
+ * in a stereo stream over I2S fits in nicely with everyone else's.
+ * (This is until the headphones codec's driver supports
+ * set_tdm_slot.)
+ *
+ * The low bclk ratio precludes transmitting more than two
+ * channels over I2S, but that's okay since there is the secondary
+ * FE for speaker arrays anyway.
+ */
+ .bclk_ratio = 64,
+ },
+ {
+ /*
+ * Secondary FE
+ *
+ * Here we want frames plenty long to be able to drive all
+ * those fancy speaker arrays.
+ */
+ .bclk_ratio = 256,
+ },
+ {
+ .is_sense = 1,
+ }
+};
+
+static void macaudio_vlimit_unlock(struct macaudio_snd_data *ma, bool unlock)
+{
+ int ret, max;
+ struct snd_kcontrol *kctl;
+ const char *suffix = volume_control_names[ma->cfg->amp];
+ size_t suffixlen;
+
+ if (!suffix) {
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ suffixlen = strlen(suffix);
+
+ switch (ma->cfg->amp) {
+ case AMP_NONE:
+ WARN_ON_ONCE(1);
+ return;
+ case AMP_TAS5770:
+ if (unlock)
+ max = TAS5770_0DB;
+ else
+ max = TAS5770_DB(ma->cfg->safe_vol);
+ break;
+ case AMP_SN012776:
+ if (unlock)
+ max = SN012776_0DB;
+ else
+ max = SN012776_DB(ma->cfg->safe_vol);
+ break;
+ case AMP_SSM3515:
+ if (unlock)
+ max = SSM3515_0DB;
+ else
+ max = SSM3515_DB(ma->cfg->safe_vol);
+ break;
+ }
+
+ /* The caller has already taken the rw semaphore as necessary */
+ list_for_each_entry(kctl, &ma->card->snd_card->controls, list) {
+ const char *suffix_start;
+ size_t namelen = strlen(kctl->id.name);
+
+ if (namelen < suffixlen)
+ continue;
+
+ suffix_start = kctl->id.name + namelen - suffixlen;
+
+ if (strncmp(suffix_start, suffix, suffixlen))
+ continue;
+
+ ret = snd_soc_limit_volume(ma->card, kctl->id.name, max);
+ if (ret < 0)
+ dev_err(ma->card->dev, "Failed to %slock volume %s: %d\n",
+ unlock ? "un" : "", kctl->id.name, ret);
+ }
+}
+
+static void macaudio_vlimit_update(struct macaudio_snd_data *ma, bool take_rwsem)
+{
+ bool unlock = true;
+ const char *reason;
+
+ /* Do nothing if there is no safety configured */
+ if (!ma->has_safety)
+ return;
+
+ /* Check that the control has been pinged within the timeout */
+ if (ma->speaker_lock_remain <= 0) {
+ reason = "Lock timeout";
+ unlock = false;
+ }
+
+ /*
+ * If this has been called from either of the safety interlock
+ * workqueues, the core API has not taken controls_rwsem for us.
+ * Take the write semaphore since macaudio_vlimit_unlock writes
+ * to the kcontrols.
+ */
+ if (take_rwsem)
+ down_write(&ma->card->snd_card->controls_rwsem);
+
+ if (unlock != ma->speaker_volume_unlocked) {
+ if (unlock) {
+ dev_info(ma->card->dev, "Speaker volumes unlocked\n");
+ } else {
+ dev_info(ma->card->dev, "Speaker volumes locked: %s\n", reason);
+ ma->speaker_volume_was_locked = true;
+ }
+
+ macaudio_vlimit_unlock(ma, unlock);
+ ma->speaker_volume_unlocked = unlock;
+ snd_ctl_notify(ma->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ma->speaker_lock_kctl->id);
+ }
+
+ if (take_rwsem)
+ up_write(&ma->card->snd_card->controls_rwsem);
+}
+
+static void macaudio_vlimit_enable_timeout(struct macaudio_snd_data *ma)
+{
+ guard(mutex)(&ma->volume_lock_mutex);
+
+ if (ma->speaker_lock_timeout_enabled) {
+ return;
+ }
+
+ if (ma->speaker_lock_remain > 0) {
+ ma->speaker_lock_timeout = ktime_add(ktime_get(), ma->speaker_lock_remain);
+ schedule_delayed_work(&ma->lock_timeout_work,
+ usecs_to_jiffies(ktime_to_us(ma->speaker_lock_remain)));
+ dev_dbg(ma->card->dev, "Enabling volume limit timeout: %ld us left\n",
+ (long)ktime_to_us(ma->speaker_lock_remain));
+ }
+
+ macaudio_vlimit_update(ma, true);
+
+ ma->speaker_lock_timeout_enabled = true;
+}
+
+static void macaudio_vlimit_disable_timeout(struct macaudio_snd_data *ma)
+{
+ ktime_t now;
+
+ guard(mutex)(&ma->volume_lock_mutex);
+
+ if (!ma->speaker_lock_timeout_enabled) {
+ return;
+ }
+
+ now = ktime_get();
+
+ cancel_delayed_work(&ma->lock_timeout_work);
+
+ if (ktime_after(now, ma->speaker_lock_timeout))
+ ma->speaker_lock_remain = 0;
+ else if (ma->speaker_lock_remain > 0)
+ ma->speaker_lock_remain = ktime_sub(ma->speaker_lock_timeout, now);
+
+ dev_dbg(ma->card->dev, "Disabling volume limit timeout: %ld us left\n",
+ (long)ktime_to_us(ma->speaker_lock_remain));
+
+ macaudio_vlimit_update(ma, true);
+
+ ma->speaker_lock_timeout_enabled = false;
+}
+
+static void macaudio_vlimit_timeout_work(struct work_struct *wrk)
+{
+ struct macaudio_snd_data *ma = container_of(to_delayed_work(wrk),
+ struct macaudio_snd_data, lock_timeout_work);
+
+ guard(mutex)(&ma->volume_lock_mutex);
+ ma->speaker_lock_remain = 0;
+ macaudio_vlimit_update(ma, true);
+}
+
+static void macaudio_vlimit_update_work(struct work_struct *wrk)
+{
+ struct macaudio_snd_data *ma = container_of(wrk,
+ struct macaudio_snd_data, lock_update_work);
+
+ guard(mutex)(&ma->be_link_mutex);
+
+ if (ma->bes_active)
+ macaudio_vlimit_enable_timeout(ma);
+ else
+ macaudio_vlimit_disable_timeout(ma);
+}
+
+static int macaudio_copy_link(struct device *dev, struct snd_soc_dai_link *target,
+ struct snd_soc_dai_link *source)
+{
+ memcpy(target, source, sizeof(struct snd_soc_dai_link));
+
+ target->cpus = devm_kmemdup(dev, target->cpus,
+ sizeof(*target->cpus) * target->num_cpus,
+ GFP_KERNEL);
+ target->codecs = devm_kmemdup(dev, target->codecs,
+ sizeof(*target->codecs) * target->num_codecs,
+ GFP_KERNEL);
+ target->platforms = devm_kmemdup(dev, target->platforms,
+ sizeof(*target->platforms) * target->num_platforms,
+ GFP_KERNEL);
+
+ if (!target->cpus || !target->codecs || !target->platforms)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int macaudio_parse_of_component(struct device_node *node, int index,
+ struct snd_soc_dai_link_component *comp)
+{
+ struct of_phandle_args args;
+ int ret;
+
+ ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells",
+ index, &args);
+ if (ret)
+ return ret;
+ comp->of_node = args.np;
+ return snd_soc_get_dai_name(&args, &comp->dai_name);
+}
+
+/*
+ * Parse one DPCM backend from the devicetree. This means taking one
+ * of the CPU DAIs and combining it with one or more CODEC DAIs.
+ */
+static int macaudio_parse_of_be_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link,
+ int be_index, int ncodecs_per_be,
+ struct device_node *cpu,
+ struct device_node *codec)
+{
+ struct snd_soc_dai_link_component *comp;
+ struct device *dev = card->dev;
+ int codec_base = be_index * ncodecs_per_be;
+ int ret, i;
+
+ link->no_pcm = 1;
+
+ link->dai_fmt = MACAUDIO_DAI_FMT;
+
+ link->num_codecs = ncodecs_per_be;
+ link->codecs = devm_kcalloc(dev, ncodecs_per_be,
+ sizeof(*comp), GFP_KERNEL);
+ link->num_cpus = 1;
+ link->cpus = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+
+ if (!link->codecs || !link->cpus)
+ return -ENOMEM;
+
+ link->num_platforms = 0;
+
+ for_each_link_codecs(link, i, comp) {
+ ret = macaudio_parse_of_component(codec, codec_base + i, comp);
+ if (ret)
+ return dev_err_probe(card->dev, ret,
+ "parsing CODEC DAI of link '%s' at %pOF\n",
+ link->name, codec);
+ }
+
+ ret = macaudio_parse_of_component(cpu, be_index, link->cpus);
+ if (ret)
+ return dev_err_probe(card->dev, ret, "parsing CPU DAI of link '%s' at %pOF\n",
+ link->name, cpu);
+
+ link->name = link->cpus[0].dai_name;
+
+ return 0;
+}
+
+static int macaudio_get_codec_idle_props(struct device *dev, struct device_node *np,
+ struct ma_codec_idle *idle, int i, int be_index,
+ int ncodecs_per_cpu)
+{
+ char propname[32];
+ const char *idle_mode;
+ int ret;
+ /* DT codec index depends on DAI BE index */
+ int sys_codec = i + (be_index * ncodecs_per_cpu);
+
+ snprintf(propname, 32, "dai-tdm-idle-mode-%d", sys_codec);
+ ret = of_property_read_string(np, propname, &idle_mode);
+ if (ret) {
+ dev_dbg(dev, "Codec %d has no idle mode property\n", sys_codec);
+ return ret;
+ }
+
+ if (!strncmp(idle_mode, "zero", 5))
+ idle->idle_mode = SND_SOC_DAI_TDM_IDLE_ZERO;
+ else if (!strncmp(idle_mode, "pulldown", 9))
+ idle->idle_mode = SND_SOC_DAI_TDM_IDLE_PULLDOWN;
+ else {
+ dev_err(dev, "Codec %d has unsupported idle mode %s\n", sys_codec, idle_mode);
+ return -EINVAL;
+ }
+
+ snprintf(propname, 32, "dai-tdm-slot-tx-idle-mask-%d", sys_codec);
+ /*
+ * Some codecs do not need a mask configured, so we don't care if
+ * this fails
+ */
+ of_property_read_u32(np, propname, &idle->idle_mask);
+
+ return 0;
+}
+
+static int macaudio_parse_of(struct macaudio_snd_data *ma, struct snd_soc_card *card)
+{
+ struct device_node *codec = NULL;
+ struct device_node *cpu = NULL;
+ struct device_node *np = NULL;
+ struct device_node *platform = NULL;
+ struct snd_soc_dai_link *link = NULL;
+ struct device *dev = card->dev;
+ struct macaudio_link_props *link_props;
+ int ret, num_links, i;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret) {
+ dev_err_probe(dev, ret, "parsing card name\n");
+ return ret;
+ }
+
+ /*
+ * Set long_name to prevent snd_soc_set_dmi_name() from setting one from
+ * make believe data u-boot provides in its SMBIOS emulation.
+ */
+ card->long_name = card->name;
+
+ /* Populate links, start with the fixed number of FE links */
+ num_links = ARRAY_SIZE(macaudio_fe_links);
+
+ /* Now add together the (dynamic) number of BE links */
+ for_each_available_child_of_node(dev->of_node, np) {
+ int num_cpus;
+
+ cpu = of_get_child_by_name(np, "cpu");
+ if (!cpu) {
+ ret = dev_err_probe(dev, -EINVAL,
+ "missing CPU DAI node at %pOF\n", np);
+ goto err_free;
+ }
+
+ num_cpus = of_count_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells");
+
+ if (num_cpus <= 0) {
+ ret = dev_err_probe(card->dev, -EINVAL,
+ "missing sound-dai property at %pOF\n", cpu);
+ goto err_free;
+ }
+ of_node_put(cpu);
+ cpu = NULL;
+
+ /* Each CPU specified counts as one BE link */
+ num_links += num_cpus;
+ }
+
+ /* Allocate the DAI link array */
+ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ ma->link_props = devm_kcalloc(dev, num_links, sizeof(*ma->link_props), GFP_KERNEL);
+ if (!card->dai_link || !ma->link_props)
+ return -ENOMEM;
+
+ link = card->dai_link;
+ link_props = ma->link_props;
+
+ for (i = 0; i < ARRAY_SIZE(macaudio_fe_links); i++) {
+ ret = macaudio_copy_link(dev, link, &macaudio_fe_links[i]);
+ if (ret)
+ goto err_free;
+
+ memcpy(link_props, &macaudio_fe_link_props[i], sizeof(struct macaudio_link_props));
+ link++; link_props++;
+ }
+
+ for (i = 0; i < num_links; i++)
+ card->dai_link[i].id = i;
+
+ /* We might disable the speakers, so count again */
+ num_links = ARRAY_SIZE(macaudio_fe_links);
+
+ /* Fill in the BEs */
+ for_each_available_child_of_node(dev->of_node, np) {
+ const char *link_name;
+ bool speakers;
+ int be_index, num_codecs, num_bes, ncodecs_per_cpu, nchannels;
+ unsigned int left_mask, right_mask;
+
+ ret = of_property_read_string(np, "link-name", &link_name);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "missing link name\n");
+ goto err_free;
+ }
+
+ dev_dbg(card->dev, "parsing link '%s'\n", link_name);
+
+ speakers = !strncmp(link_name, "Speaker", 8)
+ || !strncmp(link_name, "Speakers", 9);
+ if (speakers) {
+ if (!ma->cfg->enable_speakers) {
+ dev_err(card->dev, "Driver can't assure safety on this model, disabling speakers\n");
+ continue;
+ }
+ ma->has_speakers = 1;
+ if (ma->cfg->amp != AMP_SSM3515 && ma->cfg->safe_vol != 0)
+ ma->has_sense = 1;
+ }
+
+ cpu = of_get_child_by_name(np, "cpu");
+ codec = of_get_child_by_name(np, "codec");
+
+ if (!codec || !cpu) {
+ ret = dev_err_probe(dev, -EINVAL,
+ "missing DAI specifications for '%s'\n", link_name);
+ goto err_free;
+ }
+
+ num_bes = of_count_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells");
+ if (num_bes <= 0) {
+ ret = dev_err_probe(card->dev, -EINVAL,
+ "missing sound-dai property at %pOF\n", cpu);
+ goto err_free;
+ }
+
+ num_codecs = of_count_phandle_with_args(codec, "sound-dai",
+ "#sound-dai-cells");
+ if (num_codecs <= 0) {
+ ret = dev_err_probe(card->dev, -EINVAL,
+ "missing sound-dai property at %pOF\n", codec);
+ goto err_free;
+ }
+
+ dev_dbg(card->dev, "link '%s': %d CPUs %d CODECs\n",
+ link_name, num_bes, num_codecs);
+
+ if (num_codecs % num_bes != 0) {
+ ret = dev_err_probe(card->dev, -EINVAL,
+ "bad combination of CODEC (%d) and CPU (%d) number at %pOF\n",
+ num_codecs, num_bes, np);
+ goto err_free;
+ }
+
+ /*
+ * Now parse the cpu/codec lists into a number of DPCM backend links.
+ * In each link there will be one DAI from the cpu list paired with
+ * an evenly distributed number of DAIs from the codec list. (As is
+ * the binding semantics.)
+ */
+ ncodecs_per_cpu = num_codecs / num_bes;
+ nchannels = num_codecs * (speakers ? 1 : 2);
+
+ /* Save the max number of channels on the platform */
+ if (nchannels > ma->max_channels)
+ ma->max_channels = nchannels;
+
+ /*
+ * If there is a single speaker, assign two channels to it, because
+ * it can do downmix.
+ */
+ if (nchannels < 2)
+ nchannels = 2;
+
+ left_mask = 0;
+ for (i = 0; i < nchannels; i += 2)
+ left_mask = left_mask << 2 | 1;
+ right_mask = left_mask << 1;
+
+ for (be_index = 0; be_index < num_bes; be_index++) {
+ /*
+ * Set initial link name to be overwritten by a BE-specific
+ * name later so that we can use at least use the provisional
+ * name in error messages.
+ */
+ link->name = link_name;
+
+ ret = macaudio_parse_of_be_dai_link(card, link, be_index,
+ ncodecs_per_cpu, cpu, codec);
+ if (ret)
+ goto err_free;
+
+ link->nonatomic = link_props->is_speakers = speakers;
+ link_props->is_headphones = !speakers;
+
+ link_props->codecs = devm_kcalloc(dev, ncodecs_per_cpu,
+ sizeof(struct ma_codec_idle), GFP_KERNEL);
+ if (!link_props->codecs)
+ return -ENOMEM;
+ for (i = 0; i < ncodecs_per_cpu; i++) {
+ ret = macaudio_get_codec_idle_props(dev, np, &link_props->codecs[i],
+ i, be_index, ncodecs_per_cpu);
+
+ /* Not every codec's bus keeper will be configured */
+ if (ret)
+ dev_dbg(card->dev, "Codec has bad idle props. Skipping.\n");
+ }
+
+ if (num_bes == 2)
+ /* This sound peripheral is split between left and right BE */
+ link_props->tdm_mask = be_index ? right_mask : left_mask;
+ else
+ /* One BE covers all of the peripheral */
+ link_props->tdm_mask = left_mask | right_mask;
+
+ /* Steal platform OF reference for use in FE links later */
+ platform = link->cpus->of_node;
+
+ link++; link_props++;
+ }
+
+ of_node_put(codec);
+ of_node_put(cpu);
+ cpu = codec = NULL;
+
+ num_links += num_bes;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(macaudio_fe_links); i++)
+ card->dai_link[i].platforms->of_node = platform;
+
+ /* Skip the speaker sense PCM link if this amp has no sense (or no speakers) */
+ if (!ma->has_sense) {
+ for (i = 0; i < ARRAY_SIZE(macaudio_fe_links); i++) {
+ if (ma->link_props[i].is_sense) {
+ memmove(&card->dai_link[i], &card->dai_link[i + 1],
+ (num_links - i - 1) * sizeof(struct snd_soc_dai_link));
+ num_links--;
+ break;
+ }
+ }
+ }
+
+ card->num_links = num_links;
+
+ return 0;
+
+err_free:
+ of_node_put(codec);
+ of_node_put(cpu);
+ of_node_put(np);
+
+ if (!card->dai_link)
+ return ret;
+
+ for (i = 0; i < num_links; i++) {
+ /*
+ * TODO: If we don't go through this path are the references
+ * freed inside ASoC?
+ */
+ snd_soc_of_put_dai_link_codecs(&card->dai_link[i]);
+ snd_soc_of_put_dai_link_cpus(&card->dai_link[i]);
+ }
+
+ return ret;
+}
+
+static int macaudio_get_runtime_bclk_ratio(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dpcm *dpcm;
+
+ /*
+ * If this is a FE, look it up in link_props directly.
+ * If this is a BE, look it up in the respective FE.
+ */
+ if (!rtd->dai_link->no_pcm)
+ return ma->link_props[rtd->dai_link->id].bclk_ratio;
+
+ for_each_dpcm_fe(rtd, substream->stream, dpcm) {
+ int fe_id = dpcm->fe->dai_link->id;
+
+ return ma->link_props[fe_id].bclk_ratio;
+ }
+
+ return 0;
+}
+
+static int macaudio_dpcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ int bclk_ratio = macaudio_get_runtime_bclk_ratio(substream);
+ int i;
+
+ if (props->is_sense) {
+ /*
+ * The sense FE is opened exclusively by speakersafetyd in blocking
+ * mode. At the time of opening, this sample rate may be 0 if the
+ * speaker BE is not running. This does not matter, as speakersafetyd
+ * only cares about the active sample rate exported to the virtual
+ * sample rate kcontrol below.
+ */
+ rate->min = rate->max = cpu_dai->symmetric_rate;
+ return 0;
+ }
+
+ /* Speakers BE */
+ if (props->is_speakers) {
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* Sense PCM: keep the existing BE rate (0 if not already running) */
+ rate->min = rate->max = cpu_dai->symmetric_rate;
+
+ return 0;
+ }
+
+ /*
+ * Set the sense PCM rate control to inform userspace of the
+ * new sample rate.
+ */
+ ma->speaker_sample_rate = params_rate(params);
+ snd_ctl_notify(rtd->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ma->speaker_sample_rate_kctl->id);
+ }
+
+ if (bclk_ratio) {
+ struct snd_soc_dai *dai;
+ int mclk = params_rate(params) * bclk_ratio;
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ snd_soc_dai_set_sysclk(dai, 0, mclk, SND_SOC_CLOCK_IN);
+ snd_soc_dai_set_bclk_ratio(dai, bclk_ratio);
+ }
+
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_bclk_ratio(cpu_dai, bclk_ratio);
+ }
+
+ return 0;
+}
+
+static int macaudio_fe_startup(struct snd_pcm_substream *substream)
+{
+
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ int max_rate, ret;
+
+ if (props->is_sense) {
+ /*
+ * Sense stream will not return data while playback is inactive,
+ * so do not time out.
+ */
+ substream->wait_time = MAX_SCHEDULE_TIMEOUT;
+ return 0;
+ }
+
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 0, ma->max_channels);
+ if (ret < 0)
+ return ret;
+
+ max_rate = MACAUDIO_MAX_BCLK_FREQ / props->bclk_ratio;
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 0, max_rate);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int macaudio_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = NULL;
+ for_each_dpcm_be(rtd, substream->stream, dpcm) {
+ be = dpcm->be;
+ break;
+ }
+
+ if (!be) {
+ dev_err(rtd->dev, "opening PCM device '%s' with no audio route configured by the user\n",
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
+ return macaudio_dpcm_hw_params(substream, params);
+}
+
+
+static void macaudio_dpcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *dai;
+ int bclk_ratio = macaudio_get_runtime_bclk_ratio(substream);
+ int i;
+
+ if (bclk_ratio) {
+ for_each_rtd_codec_dais(rtd, i, dai)
+ snd_soc_dai_set_sysclk(dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+ }
+}
+
+static int macaudio_be_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ struct snd_soc_dai *dai;
+ int i;
+
+ if (props->is_speakers && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * Clear the DAI rates, so the next open can change the sample rate.
+ * This won't happen automatically if the sense PCM is open.
+ */
+ for_each_rtd_dais(rtd, i, dai) {
+ dai->symmetric_rate = 0;
+ }
+
+ /* Notify userspace that the speakers are closed */
+ ma->speaker_sample_rate = 0;
+ snd_ctl_notify(rtd->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ma->speaker_sample_rate_kctl->id);
+ }
+
+ return 0;
+}
+
+static int macaudio_be_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+
+ /* Speaker link is marked nonatomic */
+ if (props->is_speakers && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scoped_guard(mutex, &ma->be_link_mutex) {
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ma->bes_active |= BIT(rtd->dai_link->id);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ma->bes_active &= ~BIT(rtd->dai_link->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ schedule_work(&ma->lock_update_work);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops macaudio_fe_ops = {
+ .startup = macaudio_fe_startup,
+ .shutdown = macaudio_dpcm_shutdown,
+ .hw_params = macaudio_fe_hw_params,
+};
+
+static const struct snd_soc_ops macaudio_be_ops = {
+ .hw_free = macaudio_be_hw_free,
+ .shutdown = macaudio_dpcm_shutdown,
+ .hw_params = macaudio_dpcm_hw_params,
+ .trigger = macaudio_be_trigger,
+};
+
+static int macaudio_be_assign_tdm(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ struct snd_soc_dai *dai;
+ unsigned int mask;
+ int nslots, ret, i;
+
+ if (!props->tdm_mask)
+ return 0;
+
+ mask = props->tdm_mask;
+ nslots = __fls(mask) + 1;
+
+ if (rtd->dai_link->num_codecs == 1) {
+ ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), mask,
+ 0, nslots, MACAUDIO_SLOTWIDTH);
+
+ /*
+ * Headphones get a pass on -ENOTSUPP (see the comment
+ * around bclk_ratio value for primary FE).
+ */
+ if (ret == -ENOTSUPP && props->is_headphones)
+ return 0;
+
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ int slot = __ffs(mask);
+
+ mask &= ~(1 << slot);
+ ret = snd_soc_dai_set_tdm_slot(dai, 1 << slot, 0, nslots,
+ MACAUDIO_SLOTWIDTH);
+ if (ret)
+ return ret;
+
+ if (props->codecs[i].idle_mode) {
+ ret = snd_soc_dai_set_tdm_idle(dai, props->codecs[i].idle_mask,
+ 0, props->codecs[i].idle_mode, 0);
+ if (ret) {
+ dev_err(card->dev, "Failed to set idle props for codec %d\n", i);
+ return ret;
+ }
+
+ }
+ }
+
+ return 0;
+}
+
+static int macaudio_be_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ struct snd_soc_dai *dai;
+ int i, ret;
+
+ ret = macaudio_be_assign_tdm(rtd);
+ if (ret < 0)
+ return ret;
+
+ if (props->is_headphones) {
+ for_each_rtd_codec_dais(rtd, i, dai)
+ snd_soc_component_set_jack(dai->component, &macaudio_jack, NULL);
+ }
+
+ return 0;
+}
+
+static void macaudio_be_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ struct snd_soc_dai *dai;
+ int i;
+
+ if (props->is_headphones) {
+ for_each_rtd_codec_dais(rtd, i, dai)
+ snd_soc_component_set_jack(dai->component, NULL, NULL);
+ }
+}
+
+static int macaudio_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+ int nslots = props->bclk_ratio / MACAUDIO_SLOTWIDTH;
+
+ if (props->is_sense)
+ return snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0, 0xffff, 16, 16);
+
+ return snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), (1 << nslots) - 1,
+ (1 << nslots) - 1, nslots, MACAUDIO_SLOTWIDTH);
+}
+
+static struct snd_soc_jack_pin macaudio_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int macaudio_probe(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headphone Jack",
+ SND_JACK_HEADSET | SND_JACK_HEADPHONE,
+ &macaudio_jack, macaudio_jack_pins,
+ ARRAY_SIZE(macaudio_jack_pins));
+ if (ret < 0) {
+ dev_err(card->dev, "jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int macaudio_add_backend_dai_route(struct snd_soc_card *card, struct snd_soc_dai *dai,
+ bool is_speakers)
+{
+ struct snd_soc_dapm_route routes[2];
+ struct snd_soc_dapm_route *r;
+ int nroutes = 0;
+ int ret;
+
+ memset(routes, 0, sizeof(routes));
+
+ dev_dbg(card->dev, "adding routes for '%s'\n", dai->name);
+
+ r = &routes[nroutes++];
+ if (is_speakers)
+ r->source = "Speaker Playback";
+ else
+ r->source = "Headphone Playback";
+ r->sink = dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget->name;
+
+ /* If headphone jack, add capture path */
+ if (!is_speakers) {
+ r = &routes[nroutes++];
+ r->source = dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget->name;
+ r->sink = "Headset Capture";
+ }
+
+ /* If speakers, add sense capture path */
+ if (is_speakers) {
+ r = &routes[nroutes++];
+ r->source = dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget->name;
+ r->sink = "Speaker Sense Capture";
+ }
+
+ ret = snd_soc_dapm_add_routes(card->dapm, routes, nroutes);
+ if (ret)
+ dev_err(card->dev, "failed adding dynamic DAPM routes for %s\n",
+ dai->name);
+ return ret;
+}
+
+static int macaudio_add_pin_routes(struct snd_soc_card *card, struct snd_soc_component *component,
+ bool is_speakers)
+{
+ struct snd_soc_dapm_route routes[2];
+ struct snd_soc_dapm_route *r;
+ int nroutes = 0;
+ char buf[32];
+ int ret;
+
+ memset(routes, 0, sizeof(routes));
+
+ /* Connect the far ends of CODECs to pins */
+ if (is_speakers) {
+ r = &routes[nroutes++];
+ r->source = "OUT";
+ if (component->name_prefix) {
+ snprintf(buf, sizeof(buf) - 1, "%s OUT", component->name_prefix);
+ r->source = buf;
+ }
+ r->sink = "Speaker";
+ } else {
+ r = &routes[nroutes++];
+ r->source = "Jack HP";
+ r->sink = "Headphone";
+ r = &routes[nroutes++];
+ r->source = "Headset Mic";
+ r->sink = "Jack HS";
+ }
+
+ ret = snd_soc_dapm_add_routes(card->dapm, routes, nroutes);
+ if (ret)
+ dev_err(card->dev, "failed adding dynamic DAPM routes for %s\n",
+ component->name);
+ return ret;
+}
+
+#define CHECK(call, pattern, value) \
+ { \
+ int ret = call(card, pattern, value); \
+ if (ret < 0) { \
+ dev_err(card->dev, "%s on '%s': %d\n", #call, pattern, \
+ ret); \
+ return ret; \
+ } else { \
+ dev_dbg(card->dev, "%s on '%s': %d\n", #call, \
+ pattern, ret); \
+ } \
+ }
+
+#define CHECK_CONCAT(call, suffix, value) \
+ { \
+ snprintf(buf, sizeof(buf), "%s%s", prefix, suffix); \
+ CHECK(call, buf, value); \
+ }
+
+static int macaudio_set_speaker(struct snd_soc_card *card, const char *prefix, bool tweeter)
+{
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ char buf[256];
+
+ if (!ma->has_speakers)
+ return 0;
+
+ switch (ma->cfg->amp) {
+ case AMP_TAS5770:
+ if (ma->cfg->stereo) {
+ CHECK_CONCAT(snd_soc_set_enum_kctl, "ASI1 Sel", "Left");
+ CHECK_CONCAT(snd_soc_ctl_activate, "ASI1 Sel", 0);
+ }
+
+ CHECK_CONCAT(snd_soc_limit_volume, "Amp Gain Volume", ma->cfg->amp_gain);
+ break;
+ case AMP_SN012776:
+ if (ma->cfg->stereo) {
+ CHECK_CONCAT(snd_soc_set_enum_kctl, "ASI1 Sel", "Left");
+ CHECK_CONCAT(snd_soc_ctl_activate, "ASI1 Sel", 0);
+ }
+
+ CHECK_CONCAT(snd_soc_limit_volume, "Amp Gain Volume", ma->cfg->amp_gain);
+ CHECK_CONCAT(snd_soc_set_enum_kctl, "HPF Corner Frequency",
+ tweeter ? "800 Hz" : "2 Hz");
+
+ CHECK_CONCAT(snd_soc_ctl_activate, "HPF Corner Frequency", 0);
+
+ CHECK_CONCAT(snd_soc_set_enum_kctl, "OCE Handling", "Retry");
+ CHECK_CONCAT(snd_soc_ctl_activate, "OCE Handling", 0);
+ break;
+ case AMP_SSM3515:
+ /* TODO: check */
+ CHECK_CONCAT(snd_soc_set_enum_kctl, "DAC Analog Gain Select", "8.4 V Span");
+
+ CHECK_CONCAT(snd_soc_ctl_activate, "DAC Analog Gain Select", 0);
+
+ /* TODO: HPF, needs new call to set */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int macaudio_fixup_controls(struct snd_soc_card *card)
+{
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ const char *p;
+
+ /* Set the card ID early to avoid races with udev */
+ p = strrchr(card->name, ' ');
+ if (p) {
+ snprintf(card->snd_card->id, sizeof(card->snd_card->id),
+ "Apple%s", p + 1);
+ }
+
+ if (!ma->has_speakers)
+ return 0;
+
+ switch (ma->cfg->speakers) {
+ case SPKR_NONE:
+ return -EINVAL;
+ case SPKR_1W:
+ if (ma->cfg->stereo) {
+ CHECK(macaudio_set_speaker, "Left ", false);
+ CHECK(macaudio_set_speaker, "Right ", false);
+ } else {
+ CHECK(macaudio_set_speaker, "", false);
+ }
+ break;
+ case SPKR_2W:
+ CHECK(macaudio_set_speaker, "Left Front ", false);
+ CHECK(macaudio_set_speaker, "Right Front ", false);
+ CHECK(macaudio_set_speaker, "Left Rear ", false);
+ CHECK(macaudio_set_speaker, "Right Rear ", false);
+ break;
+ case SPKR_1W1T:
+ CHECK(macaudio_set_speaker, "Left Tweeter ", true);
+ CHECK(macaudio_set_speaker, "Right Tweeter ", true);
+ CHECK(macaudio_set_speaker, "Left Woofer ", false);
+ CHECK(macaudio_set_speaker, "Right Woofer ", false);
+ break;
+ case SPKR_2W1T:
+ CHECK(macaudio_set_speaker, "Left Tweeter ", true);
+ CHECK(macaudio_set_speaker, "Right Tweeter ", true);
+ CHECK(macaudio_set_speaker, "Left Woofer 1 ", false);
+ CHECK(macaudio_set_speaker, "Right Woofer 1 ", false);
+ CHECK(macaudio_set_speaker, "Left Woofer 2 ", false);
+ CHECK(macaudio_set_speaker, "Right Woofer 2 ", false);
+ break;
+ }
+
+ return 0;
+}
+
+static const char * const macaudio_spk_mux_texts[] = {
+ "Primary",
+ "Secondary"
+};
+
+SOC_ENUM_SINGLE_VIRT_DECL(macaudio_spk_mux_enum, macaudio_spk_mux_texts);
+
+static const struct snd_kcontrol_new macaudio_spk_mux =
+ SOC_DAPM_ENUM("Speaker Playback Mux", macaudio_spk_mux_enum);
+
+static const char * const macaudio_hp_mux_texts[] = {
+ "Primary",
+ "Secondary"
+};
+
+SOC_ENUM_SINGLE_VIRT_DECL(macaudio_hp_mux_enum, macaudio_hp_mux_texts);
+
+static const struct snd_kcontrol_new macaudio_hp_mux =
+ SOC_DAPM_ENUM("Headphone Playback Mux", macaudio_hp_mux_enum);
+
+static const struct snd_soc_dapm_widget macaudio_snd_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+ SND_SOC_DAPM_MUX("Speaker Playback Mux", SND_SOC_NOPM, 0, 0, &macaudio_spk_mux),
+ SND_SOC_DAPM_MUX("Headphone Playback Mux", SND_SOC_NOPM, 0, 0, &macaudio_hp_mux),
+
+ SND_SOC_DAPM_AIF_OUT("Speaker Playback", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("Headphone Playback", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("Headset Capture", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Speaker Sense Capture", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static int macaudio_sss_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+
+ return 0;
+}
+
+static int macaudio_sss_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+
+ /*
+ * TODO: Check if any locking is in order here. I would
+ * assume there is some ALSA-level lock, but DAPM implementations
+ * of kcontrol ops do explicit locking, so look into it.
+ */
+ uvalue->value.integer.value[0] = ma->speaker_sample_rate;
+
+ return 0;
+}
+
+static int macaudio_slk_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = INT_MIN;
+ uinfo->value.integer.max = INT_MAX;
+
+ return 0;
+}
+
+static int macaudio_slk_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+
+ guard(mutex)(&ma->volume_lock_mutex);
+
+ if (uvalue->value.integer.value[0] != SPEAKER_MAGIC_VALUE) {
+ return -EINVAL;
+ }
+
+ /* Serves as a notification that the lock was lost at some point */
+ if (ma->speaker_volume_was_locked) {
+ ma->speaker_volume_was_locked = false;
+ return -ETIMEDOUT;
+ }
+
+ cancel_delayed_work(&ma->lock_timeout_work);
+
+ ma->speaker_lock_remain = ms_to_ktime(SPEAKER_LOCK_TIMEOUT);
+ ma->speaker_lock_timeout = ktime_add(ktime_get(), ma->speaker_lock_remain);
+ macaudio_vlimit_update(ma, false);
+
+ if (ma->speaker_lock_timeout_enabled) {
+ dev_dbg(card->dev, "Volume limit timeout ping: %ld us left\n",
+ (long)ktime_to_us(ma->speaker_lock_remain));
+ schedule_delayed_work(&ma->lock_timeout_work,
+ usecs_to_jiffies(ktime_to_us(ma->speaker_lock_remain)));
+ }
+
+ return 0;
+}
+
+static int macaudio_slk_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+
+ uvalue->value.integer.value[0] = ma->speaker_volume_unlocked ? 1 : 0;
+
+ return 0;
+}
+
+/*
+ * Speaker limit controls go last. We only drop the unlock control,
+ * leaving sample rate, since that can be useful for safety
+ * bring-up before the kernel-side caps are ready.
+ */
+#define MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS 1
+/*
+ * If there are no speakers configured at all, we can drop both
+ * controls.
+ */
+#define MACAUDIO_NUM_SPEAKER_CONTROLS 2
+
+static const struct snd_kcontrol_new macaudio_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .name = "Speaker Sample Rate",
+ .info = macaudio_sss_info, .get = macaudio_sss_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .name = "Speaker Volume Unlock",
+ .info = macaudio_slk_info,
+ .put = macaudio_slk_put, .get = macaudio_slk_get,
+ },
+};
+
+static const struct snd_soc_dapm_route macaudio_dapm_routes[] = {
+ /* Playback paths */
+ { "Speaker Playback Mux", "Primary", "PCM0 TX" },
+ { "Speaker Playback Mux", "Secondary", "PCM1 TX" },
+ { "Speaker Playback", NULL, "Speaker Playback Mux"},
+
+ { "Headphone Playback Mux", "Primary", "PCM0 TX" },
+ { "Headphone Playback Mux", "Secondary", "PCM1 TX" },
+ { "Headphone Playback", NULL, "Headphone Playback Mux"},
+ /*
+ * Additional paths (to specific I2S ports) are added dynamically.
+ */
+
+ /* Capture paths */
+ { "PCM0 RX", NULL, "Headset Capture" },
+
+ /* Sense paths */
+ { "PCM2 RX", NULL, "Speaker Sense Capture" },
+};
+
+static int macaudio_late_probe(struct snd_soc_card *card)
+{
+ struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *dai;
+ int ret, i;
+
+ /* Add the dynamic DAPM routes */
+ for_each_card_rtds(card, rtd) {
+ struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
+
+ if (!rtd->dai_link->no_pcm)
+ continue;
+
+ for_each_rtd_cpu_dais(rtd, i, dai) {
+ ret = macaudio_add_backend_dai_route(card, dai, props->is_speakers);
+
+ if (ret)
+ return ret;
+ }
+
+ for_each_rtd_codec_dais(rtd, i, dai) {
+ ret = macaudio_add_pin_routes(card, dai->component,
+ props->is_speakers);
+
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (ma->has_speakers)
+ ma->speaker_sample_rate_kctl = snd_soc_card_get_kcontrol(card,
+ "Speaker Sample Rate");
+ if (ma->has_safety) {
+ ma->speaker_lock_kctl = snd_soc_card_get_kcontrol(card,
+ "Speaker Volume Unlock");
+
+ guard(mutex)(&ma->volume_lock_mutex);
+ macaudio_vlimit_unlock(ma, false);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_card macaudio_card = {
+ .owner = THIS_MODULE,
+ .driver_name = "macaudio",
+ .dapm_widgets = macaudio_snd_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(macaudio_snd_widgets),
+ .dapm_routes = macaudio_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(macaudio_dapm_routes),
+ .controls = macaudio_controls,
+ .num_controls = ARRAY_SIZE(macaudio_controls),
+ .probe = macaudio_probe,
+ .late_probe = macaudio_late_probe,
+ .component_chaining = true,
+ .fully_routed = true,
+ .fixup_controls = macaudio_fixup_controls,
+};
+
+/* enable amp speakers stereo gain safe_vol */
+static const struct macaudio_platform_cfg macaudio_j180_cfg = {
+ false, AMP_SN012776, SPKR_1W1T, false, 10, -20,
+};
+static const struct macaudio_platform_cfg macaudio_j274_cfg = {
+ true, AMP_TAS5770, SPKR_1W, false, 20, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j293_cfg = {
+ true, AMP_TAS5770, SPKR_2W, true, 15, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j313_cfg = {
+ true, AMP_TAS5770, SPKR_1W, true, 10, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j314_cfg = {
+ true, AMP_SN012776, SPKR_2W1T, true, 15, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j37x_j47x_cfg = {
+ true, AMP_SN012776, SPKR_1W, false, 20, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j413_cfg = {
+ true, AMP_SN012776, SPKR_1W1T, true, 15, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_j45x_cfg = {
+ false, AMP_SSM3515, SPKR_1W1T, true, 9, -20, /* TODO: gain?? */
+};
+
+static const struct macaudio_platform_cfg macaudio_j493_cfg = {
+ true, AMP_SN012776, SPKR_2W, true, 15, -20,
+};
+
+static const struct macaudio_platform_cfg macaudio_fallback_cfg = {
+ false, AMP_NONE, SPKR_NONE, false, 0, 0,
+};
+
+/*
+ * DT compatible/ID table rules:
+ *
+ * 1. Machines with **identical** speaker configurations (amps, models, chassis)
+ * are allowed to declare compatibility with the first model (chronologically),
+ * and are not enumerated in this array.
+ *
+ * 2. Machines with identical amps and speakers (=identical speaker protection
+ * rules) but a different chassis must use different compatibles, but may share
+ * the private data structure here. They are explicitly enumerated.
+ *
+ * 3. Machines with different amps or speaker layouts must use separate
+ * data structures.
+ *
+ * 4. Machines with identical speaker layouts and amps (but possibly different
+ * speaker models/chassis) may share the data structure, since only userspace
+ * cares about that (assuming our general -20dB safe level standard holds).
+ */
+static const struct of_device_id macaudio_snd_device_id[] = {
+ /* Model ID Amp Gain Speakers */
+ /* j180 AID19 sn012776 10 1× 1W+1T */
+ { .compatible = "apple,j180-macaudio", .data = &macaudio_j180_cfg },
+ /* j274 AID6 tas5770 20 1× 1W */
+ { .compatible = "apple,j274-macaudio", .data = &macaudio_j274_cfg },
+ /* j293 AID3 tas5770 15 2× 2W */
+ { .compatible = "apple,j293-macaudio", .data = &macaudio_j293_cfg },
+ /* j313 AID4 tas5770 10 2× 1W */
+ { .compatible = "apple,j313-macaudio", .data = &macaudio_j313_cfg },
+ /* j314 AID8 sn012776 15 2× 2W+1T */
+ { .compatible = "apple,j314-macaudio", .data = &macaudio_j314_cfg },
+ /* j316 AID9 sn012776 15 2× 2W+1T */
+ { .compatible = "apple,j316-macaudio", .data = &macaudio_j314_cfg },
+ /* j375 AID10 sn012776 20 1× 1W */
+ { .compatible = "apple,j375-macaudio", .data = &macaudio_j37x_j47x_cfg },
+ /* j413 AID13 sn012776 15 2× 1W+1T */
+ { .compatible = "apple,j413-macaudio", .data = &macaudio_j413_cfg },
+ /* j414 AID14 sn012776 15 2× 2W+1T Compat: apple,j314-macaudio */
+ /* j415 AID27 sn012776 15 2× 2W+1T */
+ { .compatible = "apple,j415-macaudio", .data = &macaudio_j314_cfg },
+ /* j416 AID15 sn012776 15 2× 2W+1T Compat: apple,j316-macaudio */
+ /* j456 AID5 ssm3515 15 2× 1W+1T */
+ { .compatible = "apple,j456-macaudio", .data = &macaudio_j45x_cfg },
+ /* j457 AID7 ssm3515 15 2× 1W+1T Compat: apple,j456-macaudio */
+ /* j473 AID12 sn012776 20 1× 1W */
+ { .compatible = "apple,j473-macaudio", .data = &macaudio_j37x_j47x_cfg },
+ /* j474 AID26 sn012776 20 1× 1W Compat: apple,j473-macaudio */
+ /* j475 AID25 sn012776 20 1× 1W Compat: apple,j375-macaudio */
+ /* j493 AID18 sn012776 15 2× 2W */
+ { .compatible = "apple,j493-macaudio", .data = &macaudio_j493_cfg },
+ /* Fallback, jack only */
+ { .compatible = "apple,macaudio"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, macaudio_snd_device_id);
+
+static int macaudio_snd_platform_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct macaudio_snd_data *data;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_link *link;
+ const struct of_device_id *of_id;
+ int ret;
+ int i;
+
+ of_id = of_match_device(macaudio_snd_device_id, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ card = &macaudio_card;
+ data->card = card;
+
+ snd_soc_card_set_drvdata(card, data);
+ dev_set_drvdata(&pdev->dev, data);
+ mutex_init(&data->volume_lock_mutex);
+ mutex_init(&data->be_link_mutex);
+
+ card->dev = dev;
+
+ if (of_id->data)
+ data->cfg = of_id->data;
+ else
+ data->cfg = &macaudio_fallback_cfg;
+
+ ret = macaudio_parse_of(data, card);
+ if (ret)
+ return ret;
+
+ /* Remove useless controls */
+ if (!data->has_speakers) /* No speakers, remove both */
+ card->num_controls -= MACAUDIO_NUM_SPEAKER_CONTROLS;
+ else if (!data->cfg->safe_vol) /* No safety, remove unlock */
+ card->num_controls -= MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS;
+ else /* Speakers with safety, mark us as such */
+ data->has_safety = true;
+
+ for_each_card_prelinks(card, i, link) {
+ if (link->no_pcm) {
+ link->ops = &macaudio_be_ops;
+ link->init = macaudio_be_init;
+ link->exit = macaudio_be_exit;
+ } else {
+ link->ops = &macaudio_fe_ops;
+ link->init = macaudio_fe_init;
+ }
+ }
+
+ INIT_WORK(&data->lock_update_work, macaudio_vlimit_update_work);
+ INIT_DELAYED_WORK(&data->lock_timeout_work, macaudio_vlimit_timeout_work);
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static void macaudio_snd_platform_remove(struct platform_device *pdev)
+{
+ struct macaudio_snd_data *ma = dev_get_drvdata(&pdev->dev);
+
+ cancel_work_sync(&ma->lock_update_work);
+ cancel_delayed_work_sync(&ma->lock_timeout_work);
+}
+
+static struct platform_driver macaudio_snd_driver = {
+ .probe = macaudio_snd_platform_probe,
+ .remove = macaudio_snd_platform_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = macaudio_snd_device_id,
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(macaudio_snd_driver);
+
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_DESCRIPTION("Apple Silicon Macs machine-level sound driver");
+MODULE_LICENSE("GPL");
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 08/21] ASoC: apple: Add macaudio machine driver
2026-10-18 5:54 ` [PATCH v3 08/21] ASoC: apple: Add macaudio machine driver James Calligeros
@ 2026-10-18 6:23 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:23 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [Critical] ASoC: apple: macaudio: wrong drvdata and teardown order in remove()
- [High] ASoC: apple: macaudio: ABBA deadlock in macaudio_vlimit_update()
- [High] ASoC: apple: macaudio: static macaudio_card mutated across probes
- [High] ASoC: apple: macaudio: atomic Primary FE fails with nonatomic speaker BE
- [Medium] ASoC: apple: macaudio: OF node reference leaks in macaudio_parse_of()
- [Medium] ASoC: apple: macaudio: TDM idle mode skipped in macaudio_be_assign_tdm()
- [Medium] ASoC: apple: macaudio: race with cancel_delayed_work() on timeout work
--
commit 56eeabc9bf8ef8e9b2a3eb1ec213300f3ed1ab23
Author: Martin Povišer <povik+lin@cutebit.org>
ASoC: apple: Add macaudio machine driver
This commit adds an ASoC machine driver for Apple Silicon Macs to manage
DPCM routing between MCA I2S frontends/backends, speaker arrays, speaker
sense capture, and headset jack codecs.
> diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c
> new file mode 100644
> index 0000000000000..b8e58c9a676f8
> --- /dev/null
> +++ b/sound/soc/apple/macaudio.c
> @@ -0,0 +1,1715 @@
[ ... ]
> +static struct snd_soc_dai_link macaudio_fe_links[] = {
> + {
> + .name = "Primary",
> + .stream_name = "Primary",
> + .dynamic = 1,
> + .dpcm_merged_rate = 1,
> + .dpcm_merged_chan = 1,
> + .dpcm_merged_format = 1,
> + .dai_fmt = MACAUDIO_DAI_FMT,
> + SND_SOC_DAILINK_REG(primary),
> + },
> + {
> + .name = "Secondary",
> + .stream_name = "Secondary",
> + .dynamic = 1,
> + .dpcm_merged_rate = 1,
> + .dpcm_merged_chan = 1,
> + .dpcm_merged_format = 1,
> + .dai_fmt = MACAUDIO_DAI_FMT,
> + .playback_only = 1,
> + .nonatomic = 1,
> + SND_SOC_DAILINK_REG(secondary),
> + },
[Severity: High]
Should the Primary frontend link in macaudio_fe_links[] (and the headphone
backend links in macaudio_parse_of()) also set .nonatomic = 1?
In macaudio_dapm_routes[], both Primary and Secondary are routed to both
Speaker Playback Mux (where Primary is the default selection at index 0)
and Headphone Playback Mux. In macaudio_parse_of(), speaker backend links
set link->nonatomic = 1, while headphone backend links have
link->nonatomic = 0.
When Primary (atomic) is routed to a speaker backend (nonatomic),
dpcm_be_connect() in sound/soc/soc-pcm.c rejects the connection with
-EINVAL because an atomic frontend cannot connect to a nonatomic backend:
sound/soc/soc-pcm.c:dpcm_be_connect() {
...
if (be_substream->pcm->nonatomic && !fe_substream->pcm->nonatomic) {
dev_err(be->dev, "FE is atomic but BE is nonatomic, invalid configuration\n");
return -EINVAL;
}
if (!be_substream->pcm->nonatomic && fe_substream->pcm->nonatomic) {
be_substream->pcm->nonatomic = 1;
}
...
}
Furthermore, if Secondary (nonatomic) is ever routed to Headphone Playback
Mux, dpcm_be_connect() mutates the headphone backend PCM to nonatomic = 1,
causing subsequent attempts to route Primary to headphones to also fail
with -EINVAL.
[ ... ]
> +static void macaudio_vlimit_update(struct macaudio_snd_data *ma, bool take_rwsem)
> +{
> + bool unlock = true;
> + const char *reason;
> +
> + /* Do nothing if there is no safety configured */
> + if (!ma->has_safety)
> + return;
> +
> + /* Check that the control has been pinged within the timeout */
> + if (ma->speaker_lock_remain <= 0) {
> + reason = "Lock timeout";
> + unlock = false;
> + }
> +
> + /*
> + * If this has been called from either of the safety interlock
> + * workqueues, the core API has not taken controls_rwsem for us.
> + * Take the write semaphore since macaudio_vlimit_unlock writes
> + * to the kcontrols.
> + */
> + if (take_rwsem)
> + down_write(&ma->card->snd_card->controls_rwsem);
[Severity: High]
Can taking controls_rwsem here while holding ma->volume_lock_mutex cause an
AB-BA deadlock with macaudio_slk_put()?
When lock_update_work or lock_timeout_work runs,
macaudio_vlimit_enable_timeout(), macaudio_vlimit_disable_timeout(), and
macaudio_vlimit_timeout_work() first acquire ma->volume_lock_mutex via
guard(mutex)(&ma->volume_lock_mutex) and then call
macaudio_vlimit_update(ma, true), which calls
down_write(&ma->card->snd_card->controls_rwsem):
volume_lock_mutex -> controls_rwsem
Conversely, when userspace writes to the Speaker Volume Unlock control,
snd_ctl_elem_write() in sound/core/control.c acquires
down_write(&card->controls_rwsem) before invoking macaudio_slk_put(), which
then acquires guard(mutex)(&ma->volume_lock_mutex):
controls_rwsem -> volume_lock_mutex
If a userspace control write races with either work item, will the two
threads deadlock against each other?
> +
> + if (unlock != ma->speaker_volume_unlocked) {
> + if (unlock) {
> + dev_info(ma->card->dev, "Speaker volumes unlocked\n");
> + } else {
> + dev_info(ma->card->dev, "Speaker volumes locked: %s\n", reason);
> + ma->speaker_volume_was_locked = true;
> + }
> +
> + macaudio_vlimit_unlock(ma, unlock);
> + ma->speaker_volume_unlocked = unlock;
> + snd_ctl_notify(ma->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
> + &ma->speaker_lock_kctl->id);
> + }
> +
> + if (take_rwsem)
> + up_write(&ma->card->snd_card->controls_rwsem);
> +}
> +
> +static void macaudio_vlimit_enable_timeout(struct macaudio_snd_data *ma)
> +{
> + guard(mutex)(&ma->volume_lock_mutex);
> +
> + if (ma->speaker_lock_timeout_enabled) {
> + return;
> + }
> +
> + if (ma->speaker_lock_remain > 0) {
> + ma->speaker_lock_timeout = ktime_add(ktime_get(), ma->speaker_lock_remain);
> + schedule_delayed_work(&ma->lock_timeout_work,
> + usecs_to_jiffies(ktime_to_us(ma->speaker_lock_remain)));
> + dev_dbg(ma->card->dev, "Enabling volume limit timeout: %ld us left\n",
> + (long)ktime_to_us(ma->speaker_lock_remain));
> + }
> +
> + macaudio_vlimit_update(ma, true);
> +
> + ma->speaker_lock_timeout_enabled = true;
> +}
> +
> +static void macaudio_vlimit_disable_timeout(struct macaudio_snd_data *ma)
> +{
> + ktime_t now;
> +
> + guard(mutex)(&ma->volume_lock_mutex);
> +
> + if (!ma->speaker_lock_timeout_enabled) {
> + return;
> + }
> +
> + now = ktime_get();
> +
> + cancel_delayed_work(&ma->lock_timeout_work);
> +
> + if (ktime_after(now, ma->speaker_lock_timeout))
> + ma->speaker_lock_remain = 0;
> + else if (ma->speaker_lock_remain > 0)
> + ma->speaker_lock_remain = ktime_sub(ma->speaker_lock_timeout, now);
> +
> + dev_dbg(ma->card->dev, "Disabling volume limit timeout: %ld us left\n",
> + (long)ktime_to_us(ma->speaker_lock_remain));
> +
> + macaudio_vlimit_update(ma, true);
> +
> + ma->speaker_lock_timeout_enabled = false;
> +}
> +
> +static void macaudio_vlimit_timeout_work(struct work_struct *wrk)
> +{
> + struct macaudio_snd_data *ma = container_of(to_delayed_work(wrk),
> + struct macaudio_snd_data, lock_timeout_work);
> +
> + guard(mutex)(&ma->volume_lock_mutex);
> + ma->speaker_lock_remain = 0;
> + macaudio_vlimit_update(ma, true);
> +}
[Severity: Medium]
What happens if lock_timeout_work fires and macaudio_vlimit_timeout_work()
blocks on ma->volume_lock_mutex just after macaudio_slk_put() or
macaudio_vlimit_disable_timeout() acquires ma->volume_lock_mutex?
Because macaudio_slk_put() and macaudio_vlimit_disable_timeout() use
non-synchronous cancel_delayed_work(&ma->lock_timeout_work) while holding
ma->volume_lock_mutex, cancel_delayed_work() cannot stop a
macaudio_vlimit_timeout_work() instance that has already been dispatched
and is waiting on ma->volume_lock_mutex.
Once macaudio_slk_put() refreshes ma->speaker_lock_remain to
ms_to_ktime(SPEAKER_LOCK_TIMEOUT), reschedules lock_timeout_work, and
releases ma->volume_lock_mutex (or macaudio_vlimit_disable_timeout()
disables the timeout and releases the mutex), the waiting
macaudio_vlimit_timeout_work() will acquire ma->volume_lock_mutex and
unconditionally zero ma->speaker_lock_remain and lock the speaker volumes.
Should macaudio_vlimit_timeout_work() verify that
ma->speaker_lock_timeout_enabled is still true and
ktime_after(ktime_get(), ma->speaker_lock_timeout) before zeroing
ma->speaker_lock_remain?
[ ... ]
> +static int macaudio_parse_of_component(struct device_node *node, int index,
> + struct snd_soc_dai_link_component *comp)
> +{
> + struct of_phandle_args args;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells",
> + index, &args);
> + if (ret)
> + return ret;
> + comp->of_node = args.np;
> + return snd_soc_get_dai_name(&args, &comp->dai_name);
> +}
[ ... ]
> +static int macaudio_parse_of(struct macaudio_snd_data *ma, struct snd_soc_card *card)
> +{
> + struct device_node *codec = NULL;
> + struct device_node *cpu = NULL;
> + struct device_node *np = NULL;
> + struct device_node *platform = NULL;
> + struct snd_soc_dai_link *link = NULL;
> + struct device *dev = card->dev;
> + struct macaudio_link_props *link_props;
> + int ret, num_links, i;
[ ... ]
> + for (be_index = 0; be_index < num_bes; be_index++) {
> + /*
> + * Set initial link name to be overwritten by a BE-specific
> + * name later so that we can use at least use the provisional
> + * name in error messages.
> + */
> + link->name = link_name;
> +
> + ret = macaudio_parse_of_be_dai_link(card, link, be_index,
> + ncodecs_per_cpu, cpu, codec);
> + if (ret)
> + goto err_free;
> +
> + link->nonatomic = link_props->is_speakers = speakers;
> + link_props->is_headphones = !speakers;
> +
> + link_props->codecs = devm_kcalloc(dev, ncodecs_per_cpu,
> + sizeof(struct ma_codec_idle), GFP_KERNEL);
> + if (!link_props->codecs)
> + return -ENOMEM;
[ ... ]
> + /* Steal platform OF reference for use in FE links later */
> + platform = link->cpus->of_node;
> +
> + link++; link_props++;
> + }
> +
> + of_node_put(codec);
> + of_node_put(cpu);
> + cpu = codec = NULL;
> +
> + num_links += num_bes;
> + }
[ ... ]
> +err_free:
> + of_node_put(codec);
> + of_node_put(cpu);
> + of_node_put(np);
> +
> + if (!card->dai_link)
> + return ret;
> +
> + for (i = 0; i < num_links; i++) {
> + /*
> + * TODO: If we don't go through this path are the references
> + * freed inside ASoC?
> + */
> + snd_soc_of_put_dai_link_codecs(&card->dai_link[i]);
> + snd_soc_of_put_dai_link_cpus(&card->dai_link[i]);
> + }
> +
> + return ret;
> +}
[Severity: Medium]
Does macaudio_parse_of() leak device_node references in several paths?
1. If devm_kcalloc() for link_props->codecs fails, macaudio_parse_of()
returns -ENOMEM directly from inside the for_each_available_child_of_node()
loop instead of jumping to err_free, leaking np, cpu, codec, and all
previously acquired DAI link component of_node references.
2. In macaudio_parse_of_component(), comp->of_node = args.np is assigned
before calling snd_soc_get_dai_name(&args, &comp->dai_name). In
macaudio_parse_of(), num_links is only incremented by num_bes after the
entire inner for (be_index = 0; be_index < num_bes; be_index++) loop
completes. If macaudio_parse_of_be_dai_link() fails (for example when
snd_soc_get_dai_name() returns -EPROBE_DEFER), execution jumps to err_free,
where the cleanup loop only iterates up to num_links and misses any
comp->of_node references already populated in the current child node's BE
links.
3. Regarding the TODO comment in err_free: ASoC core does not release
dai_link->codecs[].of_node or dai_link->cpus[].of_node. If
devm_snd_soc_register_card() fails or when the driver is removed, those
device_node references are never released.
[ ... ]
> +static int macaudio_be_assign_tdm(struct snd_soc_pcm_runtime *rtd)
> +{
> + struct snd_soc_card *card = rtd->card;
> + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
> + struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
> + struct snd_soc_dai *dai;
> + unsigned int mask;
> + int nslots, ret, i;
> +
> + if (!props->tdm_mask)
> + return 0;
> +
> + mask = props->tdm_mask;
> + nslots = __fls(mask) + 1;
> +
> + if (rtd->dai_link->num_codecs == 1) {
> + ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), mask,
> + 0, nslots, MACAUDIO_SLOTWIDTH);
> +
> + /*
> + * Headphones get a pass on -ENOTSUPP (see the comment
> + * around bclk_ratio value for primary FE).
> + */
> + if (ret == -ENOTSUPP && props->is_headphones)
> + return 0;
> +
> + return ret;
> + }
> +
> + for_each_rtd_codec_dais(rtd, i, dai) {
> + int slot = __ffs(mask);
> +
> + mask &= ~(1 << slot);
> + ret = snd_soc_dai_set_tdm_slot(dai, 1 << slot, 0, nslots,
> + MACAUDIO_SLOTWIDTH);
> + if (ret)
> + return ret;
> +
> + if (props->codecs[i].idle_mode) {
> + ret = snd_soc_dai_set_tdm_idle(dai, props->codecs[i].idle_mask,
> + 0, props->codecs[i].idle_mode, 0);
> + if (ret) {
> + dev_err(card->dev, "Failed to set idle props for codec %d\n", i);
> + return ret;
> + }
> +
> + }
> + }
> +
> + return 0;
> +}
[Severity: Medium]
When rtd->dai_link->num_codecs == 1, macaudio_be_assign_tdm() returns early
after calling snd_soc_dai_set_tdm_slot() without checking
props->codecs[0].idle_mode or calling snd_soc_dai_set_tdm_idle().
On machines where each speaker backend DAI link has a single codec (such as
2 speaker codecs split across 2 CPU BEs), won't this skip applying the
dai-tdm-idle-mode-* and dai-tdm-slot-tx-idle-mask-* properties parsed by
macaudio_get_codec_idle_props()?
[ ... ]
> +static struct snd_soc_card macaudio_card = {
> + .owner = THIS_MODULE,
> + .driver_name = "macaudio",
> + .dapm_widgets = macaudio_snd_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(macaudio_snd_widgets),
> + .dapm_routes = macaudio_dapm_routes,
> + .num_dapm_routes = ARRAY_SIZE(macaudio_dapm_routes),
> + .controls = macaudio_controls,
> + .num_controls = ARRAY_SIZE(macaudio_controls),
> + .probe = macaudio_probe,
> + .late_probe = macaudio_late_probe,
> + .component_chaining = true,
> + .fully_routed = true,
> + .fixup_controls = macaudio_fixup_controls,
> +};
[ ... ]
> +static int macaudio_snd_platform_probe(struct platform_device *pdev)
> +{
> + struct snd_soc_card *card;
> + struct macaudio_snd_data *data;
> + struct device *dev = &pdev->dev;
> + struct snd_soc_dai_link *link;
> + const struct of_device_id *of_id;
> + int ret;
> + int i;
> +
> + of_id = of_match_device(macaudio_snd_device_id, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> + card = &macaudio_card;
> + data->card = card;
> +
> + snd_soc_card_set_drvdata(card, data);
> + dev_set_drvdata(&pdev->dev, data);
> + mutex_init(&data->volume_lock_mutex);
> + mutex_init(&data->be_link_mutex);
> +
> + card->dev = dev;
> +
> + if (of_id->data)
> + data->cfg = of_id->data;
> + else
> + data->cfg = &macaudio_fallback_cfg;
> +
> + ret = macaudio_parse_of(data, card);
> + if (ret)
> + return ret;
> +
> + /* Remove useless controls */
> + if (!data->has_speakers) /* No speakers, remove both */
> + card->num_controls -= MACAUDIO_NUM_SPEAKER_CONTROLS;
> + else if (!data->cfg->safe_vol) /* No safety, remove unlock */
> + card->num_controls -= MACAUDIO_NUM_SPEAKER_LIMIT_CONTROLS;
> + else /* Speakers with safety, mark us as such */
> + data->has_safety = true;
[Severity: High]
Since macaudio_card is a static struct shared across probe attempts, what
happens if devm_snd_soc_register_card() defers probe (-EPROBE_DEFER) or the
device is unbound and re-probed?
In macaudio_snd_platform_probe(), card->num_controls is decremented in-place
with -= without being reset to ARRAY_SIZE(macaudio_controls). Across
repeated deferred probes, card->num_controls will be decremented multiple
times, dropping valid controls or underflowing. For example, on a machine
with speakers and !safe_vol, a second probe attempt will drop the Speaker
Sample Rate control, causing snd_soc_card_get_kcontrol(card, "Speaker Sample
Rate") in macaudio_late_probe() to return NULL and triggering a NULL pointer
dereference when macaudio_dpcm_hw_params() or macaudio_be_hw_free() accesses
&ma->speaker_sample_rate_kctl->id.
Similarly, macaudio_parse_of() assigns a devm-allocated array to
macaudio_card.dai_link; if probe fails after that allocation, devres frees
the array while leaving macaudio_card.dai_link pointing to freed memory if a
subsequent probe jumps to err_free before reallocating card->dai_link.
Embedding struct snd_soc_card inside struct macaudio_snd_data (and
initializing it from a const template on probe) would avoid mutating global
state across probes.
> +
> + for_each_card_prelinks(card, i, link) {
> + if (link->no_pcm) {
> + link->ops = &macaudio_be_ops;
> + link->init = macaudio_be_init;
> + link->exit = macaudio_be_exit;
> + } else {
> + link->ops = &macaudio_fe_ops;
> + link->init = macaudio_fe_init;
> + }
> + }
> +
> + INIT_WORK(&data->lock_update_work, macaudio_vlimit_update_work);
> + INIT_DELAYED_WORK(&data->lock_timeout_work, macaudio_vlimit_timeout_work);
> +
> + return devm_snd_soc_register_card(dev, card);
> +}
> +
> +static void macaudio_snd_platform_remove(struct platform_device *pdev)
> +{
> + struct macaudio_snd_data *ma = dev_get_drvdata(&pdev->dev);
> +
> + cancel_work_sync(&ma->lock_update_work);
> + cancel_delayed_work_sync(&ma->lock_timeout_work);
> +}
[Severity: Critical]
Does macaudio_snd_platform_remove() retrieve the wrong pointer from
dev_get_drvdata(&pdev->dev) and cancel the work items in the wrong order
relative to card unregistration?
1. In macaudio_snd_platform_probe(), dev_set_drvdata(&pdev->dev, data) sets
the platform device's drvdata to struct macaudio_snd_data *data, and then
calls devm_snd_soc_register_card(dev, card). Inside snd_soc_register_card()
in sound/soc/soc-core.c, ASoC unconditionally overwrites card->dev's drvdata
with card:
sound/soc/soc-core.c:snd_soc_register_card() {
...
dev_set_drvdata(card->dev, card);
...
}
When macaudio_snd_platform_remove() later calls dev_get_drvdata(&pdev->dev),
it receives struct snd_soc_card * (instead of struct macaudio_snd_data *)
and passes offsets inside macaudio_card to cancel_work_sync() and
cancel_delayed_work_sync().
2. Because the card is registered with devm_snd_soc_register_card(), the
devres release callback that unregisters the card runs after
macaudio_snd_platform_remove() returns. While the card is still registered
(and during PCM teardown in snd_soc_unregister_card()), macaudio_be_trigger()
or macaudio_slk_put() can schedule lock_update_work and lock_timeout_work
again after macaudio_snd_platform_remove() has completed, leading to
use-after-free when devres frees struct macaudio_snd_data.
|}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=8
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 09/21] arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (7 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 08/21] ASoC: apple: Add macaudio machine driver James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 6:04 ` sashiko-bot
2026-10-18 5:54 ` [PATCH v3 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
` (11 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The CS42L83 codec found on M1 series Apple Silicon Macs gets its voltages
from fixed regulators managed by the platform. Add these to the template
Devicetree so that they can be referenced on the relevant devices.
We do not (officially) know how the power is delivered to the chip
(voltage rail names, commoned pins, physical regulators, etc.) so
the regulators expressed here are just stand-ins.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../arm64/boot/dts/apple/t8103-jxxx.dtsi | 32 +++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
index 686fb1dd215d..7483fc5cbd20 100644
--- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
+++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
@@ -38,6 +38,38 @@ memory@800000000 {
device_type = "memory";
reg = <0x8 0 0x2 0>; /* To be filled by loader */
};
+
+ /*
+ * The CS42L83 found on all T8103 boards requires VA, VCP, VCP_FILT,
+ * VL, VD and VP regulators. We don't (officially) know how power
+ * gets to the codec, so these fixed regulators are stand-ins.
+ */
+ jack_va_reg: regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "jack-va";
+ };
+
+ /* CS42L83 VCP and VCP_FILT */
+ jack_vcp_reg: regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "jack-vcp";
+ };
+
+ jack_vl_reg: regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "jack-vl";
+ };
+
+ /* VD_FILT */
+ jack_vd_reg: regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "jack-vd";
+ };
+
+ jack_vp_reg: regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "jack-vp";
+ };
};
&serial0 {
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 09/21] arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply
2026-10-18 5:54 ` [PATCH v3 09/21] arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply James Calligeros
@ 2026-10-18 6:04 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:04 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] arm64: dts: apple: duplicate node names in t8103-jxxx.dtsi merge regulators
--
commit d0005942339444378649a8aca2fe8ee2e51f9a6a
Author: James Calligeros <jcalligeros99@gmail.com>
arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply
This commit adds five fixed-regulator nodes to the Apple T8103 template
devicetree to serve as stand-in power supplies for the CS42L83 codec.
> diff --git a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
> index 686fb1dd215d2..7483fc5cbd204 100644
> --- a/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
> +++ b/arch/arm64/boot/dts/apple/t8103-jxxx.dtsi
> @@ -38,6 +38,38 @@ memory@800000000 {
> device_type = "memory";
> reg = <0x8 0 0x2 0>; /* To be filled by loader */
> };
> +
> + /*
> + * The CS42L83 found on all T8103 boards requires VA, VCP, VCP_FILT,
> + * VL, VD and VP regulators. We don't (officially) know how power
> + * gets to the codec, so these fixed regulators are stand-ins.
> + */
> + jack_va_reg: regulator {
> + compatible = "regulator-fixed";
> + regulator-name = "jack-va";
> + };
> +
> + /* CS42L83 VCP and VCP_FILT */
> + jack_vcp_reg: regulator {
> + compatible = "regulator-fixed";
> + regulator-name = "jack-vcp";
> + };
> +
> + jack_vl_reg: regulator {
> + compatible = "regulator-fixed";
> + regulator-name = "jack-vl";
> + };
> +
> + /* VD_FILT */
> + jack_vd_reg: regulator {
> + compatible = "regulator-fixed";
> + regulator-name = "jack-vd";
> + };
> +
> + jack_vp_reg: regulator {
> + compatible = "regulator-fixed";
> + regulator-name = "jack-vp";
> + };
[Severity: Medium]
Won't giving all five sibling nodes under / the identical node name
"regulator" cause dtc to merge them into a single /regulator node?
When a T8103 board DTS includes t8103.dtsi (which defines /) followed by
t8103-jxxx.dtsi (which re-opens /), dtc merges the two root nodes via
merge_nodes() in scripts/dtc/livetree.c:
for_each_child_withdel(old_node, old_child) {
if (streq(old_child->name, new_child->name)) {
merge_nodes(old_child, new_child);
new_child = NULL;
break;
}
}
if (new_child)
add_child(old_node, new_child);
The first child named "regulator" (jack_va_reg) is added to /, and the
remaining four sibling nodes named "regulator" (jack_vcp_reg, jack_vl_reg,
jack_vd_reg, and jack_vp_reg) match streq(old_child->name, new_child->name)
and are merged into that single /regulator node. As a result, all five
labels resolve to the same node and each definition overwrites
regulator-name until only "jack-vp" remains.
Should each node be given a distinct name matching
Documentation/devicetree/bindings/regulator/fixed-regulator.yaml, such as
regulator-jack-va, regulator-jack-vcp, regulator-jack-vl,
regulator-jack-vd, and regulator-jack-vp?
> };
>
> &serial0 {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=9
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (8 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 09/21] arm64: dts: apple: t8103-jxxx: Add regulators for CS42L83 power supply James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 6:10 ` sashiko-bot
2026-10-18 5:54 ` [PATCH v3 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
` (10 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 Mac mini integrates Apple-specific variants of the TI TAS2770
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8103-j274.dts | 72 +++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts
index 1212852251e0..150d778565f0 100644
--- a/arch/arm64/boot/dts/apple/t8103-j274.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j274.dts
@@ -75,4 +75,76 @@ &i2c2 {
status = "okay";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker: codec@31 {
+ compatible = "ti,tas5770l";
+ reg = <0x31>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ jack_codec: codec@48 {
+ compatible = "cirrus,cs42l83";
+ reg = <0x48>;
+ interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ cirrus,ts-inv = <1>;
+ VA-supply = <&jack_va_reg>;
+ VP-supply = <&jack_vp_reg>;
+ VCP-supply = <&jack_vcp_reg>;
+ VD_FILT-supply = <&jack_vd_reg>;
+ VL-supply = <&jack_vl_reg>;
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j274-macaudio";
+ model = "Mac mini J274";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ link-name = "Speaker";
+
+ codec {
+ sound-dai = <&speaker>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-mini.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes
2026-10-18 5:54 ` [PATCH v3 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
@ 2026-10-18 6:10 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:10 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] arm64: dts: apple: duplicate regulator node names merge into single node
- [Low] arm64: dts: apple: t8103-j274: duplicate &i2c2 node reference
--
commit 2140e74bb313811cd8c26e108fe31118f7e0ab3f
Author: James Calligeros <jcalligeros99@gmail.com>
arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes
This commit adds Device Tree nodes for the TI TAS5770L speaker codec on
i2c1, the Cirrus CS42L83 headset jack codec on i2c2, and the top-level
sound node for the M1 Mac mini (J274).
> diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts
> index 1212852251e07..150d778565f0c 100644
> --- a/arch/arm64/boot/dts/apple/t8103-j274.dts
> +++ b/arch/arm64/boot/dts/apple/t8103-j274.dts
> @@ -75,4 +75,76 @@ &i2c2 {
> status = "okay";
> };
>
> +/* Audio codecs */
> +&i2c1 {
> + status = "okay";
> +
> + speaker: codec@31 {
> + compatible = "ti,tas5770l";
> + reg = <0x31>;
> + interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
> + shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
> + #sound-dai-cells = <0>;
> + ti,imon-slot-no = <0>;
> + ti,vmon-slot-no = <2>;
> + };
> +};
> +
> +&i2c2 {
> + status = "okay";
[Severity: Low]
Does this duplicate the existing &i2c2 block right above? Since
arch/arm64/boot/dts/apple/t8103-j274.dts already has an &i2c2 block
setting status = "okay", could jack_codec be placed inside that existing
block (or the earlier stub removed) to avoid referencing &i2c2 twice in
the same file?
> +
> + jack_codec: codec@48 {
> + compatible = "cirrus,cs42l83";
> + reg = <0x48>;
> + interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
> + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
> + #sound-dai-cells = <0>;
> + sound-name-prefix = "Jack";
> + cirrus,ts-inv = <1>;
> + VA-supply = <&jack_va_reg>;
> + VP-supply = <&jack_vp_reg>;
> + VCP-supply = <&jack_vcp_reg>;
> + VD_FILT-supply = <&jack_vd_reg>;
> + VL-supply = <&jack_vl_reg>;
> + };
> +};
[Severity: Medium]
Do all five regulator supplies in jack_codec end up resolving to a single
merged /regulator node in the compiled DTB?
In arch/arm64/boot/dts/apple/t8103-jxxx.dtsi (included by t8103-j274.dts
after t8103.dtsi), all five fixed regulators under the root node / use the
identical node name regulator:
jack_va_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-va";
};
/* CS42L83 VCP and VCP_FILT */
jack_vcp_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vcp";
};
jack_vl_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vl";
};
/* VD_FILT */
jack_vd_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vd";
};
jack_vp_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vp";
};
When dtc merges the root node / from t8103-jxxx.dtsi into the existing
root node from t8103.dtsi via merge_nodes() in scripts/dtc/livetree.c, the
first regulator child (jack_va_reg) is added to old_node->children, and
each of the remaining four regulator children matches that node name and
merges into it before check_duplicate_node_names() runs.
As a result, four of the five regulators (jack-va, jack-vcp, jack-vl,
jack-vd) are overwritten, leaving a single /regulator node with
regulator-name = "jack-vp" and all five labels pointing to the same
phandle. Should the regulator nodes in t8103-jxxx.dtsi be given distinct
node names such as regulator-jack-va, regulator-jack-vcp,
regulator-jack-vl, regulator-jack-vd, and regulator-jack-vp?
[ ... ]
>
> --
> 2.51.2
>
base-commit: 74612d0b448fe745e80cda87fe49ac3c102ccc1f
--
2.51.2
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=10
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 11/21] arm64: dts: apple: t8103-j313: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (9 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 6:15 ` sashiko-bot
2026-10-18 5:54 ` [PATCH v3 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
` (9 subsequent siblings)
20 siblings, 1 reply; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 MacBook Air integrates Apple-specific variants of the TI TAS2770
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8103-j313.dts | 87 +++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts
index f8b2b1637b9d..ed0eb358809b 100644
--- a/arch/arm64/boot/dts/apple/t8103-j313.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j313.dts
@@ -55,4 +55,91 @@ &typec1 {
label = "USB-C Left-front";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left: codec@31 {
+ compatible = "ti,tas5770l";
+ reg = <0x31>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right: codec@34 {
+ compatible = "ti,tas5770l";
+ reg = <0x34>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+
+ jack_codec: codec@48 {
+ compatible = "cirrus,cs42l83";
+ reg = <0x48>;
+ interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ cirrus,ts-inv = <1>;
+ VA-supply = <&jack_va_reg>;
+ VP-supply = <&jack_vp_reg>;
+ VCP-supply = <&jack_vcp_reg>;
+ VD_FILT-supply = <&jack_vd_reg>;
+ VL-supply = <&jack_vl_reg>;
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j313-macaudio";
+ model = "MacBook Air J313";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-1 = "zero";
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left>, <&speaker_right>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-laptop.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 11/21] arm64: dts: apple: t8103-j313: Add speaker/headset jack nodes
2026-10-18 5:54 ` [PATCH v3 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
@ 2026-10-18 6:15 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-10-18 6:15 UTC (permalink / raw)
To: James Calligeros; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] ASoC: apple: macaudio: macaudio_be_assign_tdm() skips idle_mode for 1-codec BE
- [Medium] arm64: dts: apple: t8103-j313: jack_codec supplies merge into one /regulator
- [Medium] ASoC: apple: macaudio: unreleased OF node refs in macaudio_parse_of()
--
commit 802339e5380e3a87c88c58c583fe94106f5d49a8
Author: James Calligeros <jcalligeros99@gmail.com>
arm64: dts: apple: t8103-j313: Add speaker/headset jack nodes
This commit adds the TI TAS5770L speaker amplifier nodes and Cirrus CS42L83
headset jack codec node to the M1 MacBook Air (J313) device tree and wires
them to the macaudio machine driver via the sound node.
> diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts
> index f8b2b1637b9dc..ed0eb358809b4 100644
> --- a/arch/arm64/boot/dts/apple/t8103-j313.dts
> +++ b/arch/arm64/boot/dts/apple/t8103-j313.dts
> @@ -55,4 +55,91 @@ &typec1 {
[ ... ]
> + jack_codec: codec@48 {
> + compatible = "cirrus,cs42l83";
> + reg = <0x48>;
> + interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
> + reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
> + #sound-dai-cells = <0>;
> + sound-name-prefix = "Jack";
> + cirrus,ts-inv = <1>;
> + VA-supply = <&jack_va_reg>;
> + VP-supply = <&jack_vp_reg>;
> + VCP-supply = <&jack_vcp_reg>;
> + VD_FILT-supply = <&jack_vd_reg>;
> + VL-supply = <&jack_vl_reg>;
> + };
> +};
[Severity: Medium]
Do these five supply phandles on jack_codec resolve to five distinct
regulators in the compiled DTB?
In arch/arm64/boot/dts/apple/t8103-jxxx.dtsi (included by t8103-j313.dts),
all five fixed regulators are defined as direct children of / with the
identical node name regulator:
arch/arm64/boot/dts/apple/t8103-jxxx.dtsi:
jack_va_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-va";
};
/* CS42L83 VCP and VCP_FILT */
jack_vcp_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vcp";
};
jack_vl_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vl";
};
/* VD_FILT */
jack_vd_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vd";
};
jack_vp_reg: regulator {
compatible = "regulator-fixed";
regulator-name = "jack-vp";
};
Because they share the same /regulator path, dtc merges all five definitions
into a single node, overwriting regulator-name to "jack-vp" and attaching
all five labels to that single node, so VA-supply, VP-supply, VCP-supply,
VD_FILT-supply, and VL-supply all point to the same regulator instance.
> +
> +/ {
> + sound: sound {
> + compatible = "apple,j313-macaudio";
> + model = "MacBook Air J313";
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dai-link@0 {
> + reg = <0>;
> +
> + dai-tdm-idle-mode-0 = "zero";
> + dai-tdm-idle-mode-1 = "zero";
> +
> + link-name = "Speakers";
> +
> + codec {
> + sound-dai = <&speaker_left>, <&speaker_right>;
> + };
> +
> + cpu {
> + sound-dai = <&mca 0>, <&mca 1>;
> + };
> + };
[Severity: Medium]
Will dai-tdm-idle-mode-0 and dai-tdm-idle-mode-1 actually be applied to
speaker_left and speaker_right here?
In dai-link@0, there are 2 codecs (<&speaker_left>, <&speaker_right>) and
2 CPU DAIs (<&mca 0>, <&mca 1>). When macaudio_snd_platform_probe() ->
macaudio_parse_of() parses this node in sound/soc/apple/macaudio.c,
ncodecs_per_cpu is num_codecs / num_bes = 2 / 2 = 1. Two backend DAI links
are created with num_codecs = 1, and macaudio_get_codec_idle_props() stores
dai-tdm-idle-mode-0 and dai-tdm-idle-mode-1 into each backend link's
link_props->codecs[0].idle_mode.
However, when macaudio_be_init() -> macaudio_be_assign_tdm() runs, it takes
the single-codec early return without configuring idle_mode:
sound/soc/apple/macaudio.c:macaudio_be_assign_tdm() {
...
if (rtd->dai_link->num_codecs == 1) {
ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), mask,
0, nslots, MACAUDIO_SLOTWIDTH);
/*
* Headphones get a pass on -ENOTSUPP (see the comment
* around bclk_ratio value for primary FE).
*/
if (ret == -ENOTSUPP && props->is_headphones)
return 0;
return ret;
}
...
}
Because macaudio_be_assign_tdm() returns before calling
snd_soc_dai_set_tdm_idle(), tas2770_set_dai_tdm_idle() is never called for
either speaker amplifier on J313, leaving the SDOUT TDM idle slots
unconfigured while both codecs share the IV-sense capture TDM bus.
[Severity: Medium]
Also, can binding apple,j313-macaudio with this multi-BE dai-link@0 leak
device_node references in macaudio_parse_of() when probing defers or fails,
or on driver unbind?
First, in macaudio_parse_of_component(), of_parse_phandle_with_args()
acquires a reference on args.np and assigns it to comp->of_node before
calling snd_soc_get_dai_name():
sound/soc/apple/macaudio.c:macaudio_parse_of_component() {
ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells",
index, &args);
if (ret)
return ret;
comp->of_node = args.np;
return snd_soc_get_dai_name(&args, &comp->dai_name);
}
When speaker_left, speaker_right, jack_codec, or mca has not registered its
ASoC component yet, snd_soc_get_dai_name() returns -EPROBE_DEFER while
comp->of_node still holds the reference. In macaudio_parse_of(), the
for (be_index = 0; be_index < num_bes; be_index++) loop populates
card->dai_link[num_links + be_index], but num_links is only incremented by
num_bes after the entire be_index loop finishes:
sound/soc/apple/macaudio.c:macaudio_parse_of() {
for (be_index = 0; be_index < num_bes; be_index++) {
...
ret = macaudio_parse_of_be_dai_link(card, link, be_index,
ncodecs_per_cpu, cpu, codec);
if (ret)
goto err_free;
...
link_props->codecs = devm_kcalloc(dev, ncodecs_per_cpu,
sizeof(struct ma_codec_idle), GFP_KERNEL);
if (!link_props->codecs)
return -ENOMEM;
...
}
...
num_links += num_bes;
}
When macaudio_parse_of_be_dai_link() jumps to err_free, the cleanup loop
only iterates up to num_links, missing the comp->of_node references acquired
in card->dai_link[num_links .. num_links + be_index] during the failing
iteration:
sound/soc/apple/macaudio.c:macaudio_parse_of() {
err_free:
of_node_put(codec);
of_node_put(cpu);
of_node_put(np);
if (!card->dai_link)
return ret;
for (i = 0; i < num_links; i++) {
snd_soc_of_put_dai_link_codecs(&card->dai_link[i]);
snd_soc_of_put_dai_link_cpus(&card->dai_link[i]);
}
}
Second, if devm_kcalloc() for link_props->codecs fails inside
for_each_available_child_of_node(dev->of_node, np), macaudio_parse_of()
returns -ENOMEM directly instead of jumping to err_free, leaking np, cpu,
codec, and all previously parsed DAI link component node references.
Finally, once macaudio_parse_of() succeeds, neither a failure in
devm_snd_soc_register_card() in macaudio_snd_platform_probe() nor
macaudio_snd_platform_remove() calls snd_soc_of_put_dai_link_codecs() and
snd_soc_of_put_dai_link_cpus() on the backend DAI links.
[ ... ]
> --
> 2.43.0
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018-macaudio-v3-0-abdff7741dea@gmail.com?part=11
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 12/21] arm64: dts: apple: t8103-j293: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (10 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
` (8 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 MacBook Pro integrates Apple-specific variants of the TI TAS2770
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8103-j293.dts | 116 +++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts
index 1d074b9e6018..7d4907cefca9 100644
--- a/arch/arm64/boot/dts/apple/t8103-j293.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j293.dts
@@ -133,5 +133,121 @@ &displaydfr_dart {
status = "okay";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left_rear: codec@31 {
+ compatible = "ti,tas5770l";
+ reg = <0x31>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Rear";
+ ti,imon-slot-no = <8>;
+ ti,vmon-slot-no = <10>;
+ };
+
+ speaker_left_front: codec@32 {
+ compatible = "ti,tas5770l";
+ reg = <0x32>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Front";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ jack_codec: codec@48 {
+ compatible = "cirrus,cs42l83";
+ reg = <0x48>;
+ interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ cirrus,ts-inv = <1>;
+ VA-supply = <&jack_va_reg>;
+ VP-supply = <&jack_vp_reg>;
+ VCP-supply = <&jack_vcp_reg>;
+ VD_FILT-supply = <&jack_vd_reg>;
+ VL-supply = <&jack_vl_reg>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right_rear: codec@34 {
+ compatible = "ti,tas5770l";
+ reg = <0x34>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Rear";
+ ti,imon-slot-no = <12>;
+ ti,vmon-slot-no = <14>;
+ };
+
+ speaker_right_front: codec@35 {
+ compatible = "ti,tas5770l";
+ reg = <0x35>;
+ interrupts-extended = <&pinctrl_ap 182 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 181 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Front";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j293-macaudio";
+ model = "MacBook Pro J293";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "pulldown";
+ dai-tdm-idle-mode-2 = "pulldown";
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left_front>,
+ <&speaker_left_rear>,
+ <&speaker_right_front>,
+ <&speaker_right_rear>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-laptop.dtsi"
#include "hwmon-fan.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 13/21] arm64: dts: apple: t8103-j45x: Add headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (11 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
` (7 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 iMacs integrate Apple-specific variants of the TI TAS2770
and Cirrus CS42L42. Since it is not yet safe to expose the speakers
on these machines, add the headset jack nodes and advertise them
to the machine driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8103-j456.dts | 43 +++++++++++++++++++++++++
arch/arm64/boot/dts/apple/t8103-j457.dts | 43 +++++++++++++++++++++++++
2 files changed, 86 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts
index 090c97bb781b..91da90b37089 100644
--- a/arch/arm64/boot/dts/apple/t8103-j456.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j456.dts
@@ -88,3 +88,46 @@ &pcie0_dart_1 {
&pcie0_dart_2 {
status = "okay";
};
+
+&i2c1 {
+ status = "okay";
+
+ jack_codec: codec@48 {
+ compatible = "cirrus,cs42l83";
+ reg = <0x48>;
+ interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ cirrus,ts-inv = <1>;
+ VA-supply = <&jack_va_reg>;
+ VP-supply = <&jack_vp_reg>;
+ VCP-supply = <&jack_vcp_reg>;
+ VD_FILT-supply = <&jack_vd_reg>;
+ VL-supply = <&jack_vl_reg>;
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j456-macaudio";
+ model = "iMac J456";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/apple/t8103-j457.dts b/arch/arm64/boot/dts/apple/t8103-j457.dts
index ebddde75455c..3e8d7648b3ac 100644
--- a/arch/arm64/boot/dts/apple/t8103-j457.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j457.dts
@@ -69,3 +69,46 @@ ethernet0: ethernet@0,0 {
&pcie0_dart_2 {
status = "okay";
};
+
+&i2c1 {
+ status = "okay";
+
+ jack_codec: codec@48 {
+ compatible = "cirrus,cs42l83";
+ reg = <0x48>;
+ interrupts-extended = <&pinctrl_ap 183 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 11 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ cirrus,ts-inv = <1>;
+ VA-supply = <&jack_va_reg>;
+ VP-supply = <&jack_vp_reg>;
+ VCP-supply = <&jack_vcp_reg>;
+ VD_FILT-supply = <&jack_vd_reg>;
+ VL-supply = <&jack_vl_reg>;
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j457-macaudio", "apple,j456-macaudio";
+ model = "iMac J457";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (12 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
` (6 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 MacBook Air integrates Apple-specific variants of the TI TAS2764
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8112-j413.dts | 108 +++++++++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8112-j413.dts b/arch/arm64/boot/dts/apple/t8112-j413.dts
index 7ccfceb08093..5f8768832e1e 100644
--- a/arch/arm64/boot/dts/apple/t8112-j413.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j413.dts
@@ -92,4 +92,112 @@ &fpwm1 {
status = "okay";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left_woof: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Woofer";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+
+ speaker_left_tweet: codec@39 {
+ compatible = "ti,sn012776";
+ reg = <0x39>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Tweeter";
+ ti,imon-slot-no = <8>;
+ ti,vmon-slot-no = <10>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right_woof: codec@3b {
+ compatible = "ti,sn012776";
+ reg = <0x3b>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Woofer";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+
+ speaker_right_tweet: codec@3c {
+ compatible = "ti,sn012776";
+ reg = <0x3c>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Tweeter";
+ ti,imon-slot-no = <12>;
+ ti,vmon-slot-no = <14>;
+ };
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j413-macaudio";
+ model = "MacBook Air J413";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-2 = "zero";
+ dai-tdm-slot-tx-idle-mask-0 = <0xf0f0>;
+ dai-tdm-slot-tx-idle-mask-2 = <0x0f0f>;
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left_woof>,
+ <&speaker_left_tweet>,
+ <&speaker_right_woof>,
+ <&speaker_right_tweet>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-laptop.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 15/21] arm64: dts: apple: t8112-j415: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (13 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
` (5 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 MacBook Air integrates Apple-specific variants of the TI TAS2764
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8112-j415.dts | 132 +++++++++++++++++++++++++
1 file changed, 132 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8112-j415.dts b/arch/arm64/boot/dts/apple/t8112-j415.dts
index fdc3409d2e07..099b9837b633 100644
--- a/arch/arm64/boot/dts/apple/t8112-j415.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j415.dts
@@ -92,4 +92,136 @@ &fpwm1 {
status = "okay";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left_woof1: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Woofer 1";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+
+ speaker_left_tweet: codec@39 {
+ compatible = "ti,sn012776";
+ reg = <0x39>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Tweeter";
+ ti,imon-slot-no = <8>;
+ ti,vmon-slot-no = <10>;
+ };
+
+ speaker_left_woof2: codec@3a {
+ compatible = "ti,sn012776";
+ reg = <0x3a>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Woofer 2";
+ ti,imon-slot-no = <16>;
+ ti,vmon-slot-no = <18>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right_woof1: codec@3b {
+ compatible = "ti,sn012776";
+ reg = <0x3b>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Woofer 1";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+
+ speaker_right_tweet: codec@3c {
+ compatible = "ti,sn012776";
+ reg = <0x3c>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Tweeter";
+ ti,imon-slot-no = <12>;
+ ti,vmon-slot-no = <14>;
+ };
+
+ speaker_right_woof2: codec@3d {
+ compatible = "ti,sn012776";
+ reg = <0x3d>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Woofer 2";
+ ti,imon-slot-no = <20>;
+ ti,vmon-slot-no = <22>;
+ };
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j415-macaudio";
+ model = "MacBook Air J415";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-3 = "zero";
+ dai-tdm-slot-tx-idle-mask-0 = <0xf0f0f0>;
+ dai-tdm-slot-tx-idle-mask-3 = <0x0f0f0f>;
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left_woof1>,
+ <&speaker_left_tweet>,
+ <&speaker_left_woof2>,
+ <&speaker_right_woof1>,
+ <&speaker_right_tweet>,
+ <&speaker_right_woof2>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-laptop.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 16/21] arm64: dts: apple: t8112-j473: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (14 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
` (4 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 Mac mini integrates Apple-specific variants of the TI TAS2764
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8112-j473.dts | 62 +++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8112-j473.dts b/arch/arm64/boot/dts/apple/t8112-j473.dts
index 105a9d118980..36f06c6e385d 100644
--- a/arch/arm64/boot/dts/apple/t8112-j473.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j473.dts
@@ -85,4 +85,66 @@ &typec1 {
label = "USB-C Back-right";
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j473-macaudio";
+ model = "Mac mini J473";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-mini.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 17/21] arm64: dts: apple: t8112-j493: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (15 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
` (3 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 MacBook Pro integrates Apple-specific variants of the TI TAS2764
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
arch/arm64/boot/dts/apple/t8112-j493.dts | 108 +++++++++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t8112-j493.dts b/arch/arm64/boot/dts/apple/t8112-j493.dts
index 7cea5c594810..8993775da9ab 100644
--- a/arch/arm64/boot/dts/apple/t8112-j493.dts
+++ b/arch/arm64/boot/dts/apple/t8112-j493.dts
@@ -147,5 +147,113 @@ touchbar0: touchbar@0 {
};
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left_rear: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Rear";
+ ti,imon-slot-no = <8>;
+ ti,vmon-slot-no = <10>;
+ };
+
+ speaker_left_front: codec@39 {
+ compatible = "ti,sn012776";
+ reg = <0x39>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Front";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right_rear: codec@3b {
+ compatible = "ti,sn012776";
+ reg = <0x3b>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Rear";
+ ti,imon-slot-no = <12>;
+ ti,vmon-slot-no = <14>;
+ };
+
+ speaker_right_front: codec@3c {
+ compatible = "ti,sn012776";
+ reg = <0x3c>;
+ interrupts-extended = <&pinctrl_ap 11 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 88 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Front";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 149 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 12 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
+/ {
+ sound: sound {
+ compatible = "apple,j493-macaudio", "apple,j293-macaudio";
+ model = "MacBook Pro J493";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-2 = "zero";
+ dai-tdm-slot-tx-idle-mask-0 = <0xf0f0>;
+ dai-tdm-slot-tx-idle-mask-2 = <0x0f0f>;
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left_front>,
+ <&speaker_left_rear>,
+ <&speaker_right_front>,
+ <&speaker_right_rear>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "hwmon-laptop.dtsi"
#include "hwmon-fan.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 18/21] arm64: dts: apple: t600x-j31x: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (16 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
` (2 subsequent siblings)
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 Pro/Max MacBooks integrate Apple-specific variants of the TI TAS2764
and Cirrus CS42L42. Add these nodes and advertise them to the machine
driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../arm64/boot/dts/apple/t6000-j314s.dts | 5 +
.../arm64/boot/dts/apple/t6000-j316s.dts | 5 +
.../arm64/boot/dts/apple/t6001-j314c.dts | 5 +
.../arm64/boot/dts/apple/t6001-j316c.dts | 5 +
.../boot/dts/apple/t600x-j314-j316.dtsi | 135 +++++++++++++++++++++++++
5 files changed, 155 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t6000-j314s.dts b/arch/arm64/boot/dts/apple/t6000-j314s.dts
index 1430b91ff1b1..63ae47ea46e6 100644
--- a/arch/arm64/boot/dts/apple/t6000-j314s.dts
+++ b/arch/arm64/boot/dts/apple/t6000-j314s.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,maldives";
};
+
+&sound {
+ compatible = "apple,j314-macaudio";
+ model = "MacBook Pro J314";
+};
diff --git a/arch/arm64/boot/dts/apple/t6000-j316s.dts b/arch/arm64/boot/dts/apple/t6000-j316s.dts
index da0cbe7d9673..c8428fbf9244 100644
--- a/arch/arm64/boot/dts/apple/t6000-j316s.dts
+++ b/arch/arm64/boot/dts/apple/t6000-j316s.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,madagascar";
};
+
+&sound {
+ compatible = "apple,j316-macaudio";
+ model = "MacBook Pro J316";
+};
diff --git a/arch/arm64/boot/dts/apple/t6001-j314c.dts b/arch/arm64/boot/dts/apple/t6001-j314c.dts
index c37097dcfdb3..05066cbde96b 100644
--- a/arch/arm64/boot/dts/apple/t6001-j314c.dts
+++ b/arch/arm64/boot/dts/apple/t6001-j314c.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,maldives";
};
+
+&sound {
+ compatible = "apple,j314-macaudio";
+ model = "MacBook Pro J314";
+};
diff --git a/arch/arm64/boot/dts/apple/t6001-j316c.dts b/arch/arm64/boot/dts/apple/t6001-j316c.dts
index 3bc6e0c3294c..ee0339b61713 100644
--- a/arch/arm64/boot/dts/apple/t6001-j316c.dts
+++ b/arch/arm64/boot/dts/apple/t6001-j316c.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,madagascar";
};
+
+&sound {
+ compatible = "apple,j316-macaudio";
+ model = "MacBook Pro J316";
+};
diff --git a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
index caded1636f3d..1f23ae3e4e1a 100644
--- a/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
+++ b/arch/arm64/boot/dts/apple/t600x-j314-j316.dtsi
@@ -169,6 +169,94 @@ hpm5: usb-pd@3a {
};
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker_left_woof1: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Woofer 1";
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+
+ speaker_left_woof2: codec@39 {
+ compatible = "ti,sn012776";
+ reg = <0x39>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Woofer 2";
+ ti,imon-slot-no = <16>;
+ ti,vmon-slot-no = <18>;
+ };
+
+ speaker_left_tweet: codec@3a {
+ compatible = "ti,sn012776";
+ reg = <0x3a>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Tweeter";
+ ti,imon-slot-no = <8>;
+ ti,vmon-slot-no = <10>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
+&i2c3 {
+ status = "okay";
+
+ speaker_right_woof1: codec@3b {
+ compatible = "ti,sn012776";
+ reg = <0x3b>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Woofer 1";
+ ti,imon-slot-no = <4>;
+ ti,vmon-slot-no = <6>;
+ };
+
+ speaker_right_woof2: codec@3c {
+ compatible = "ti,sn012776";
+ reg = <0x3c>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Woofer 2";
+ ti,imon-slot-no = <20>;
+ ti,vmon-slot-no = <22>;
+ };
+
+ speaker_right_tweet: codec@3d {
+ compatible = "ti,sn012776";
+ reg = <0x3d>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Tweeter";
+ ti,imon-slot-no = <12>;
+ ti,vmon-slot-no = <14>;
+ };
+};
+
&nco_clkref {
clock-frequency = <1068000000>;
};
@@ -366,6 +454,53 @@ &atcphy3 {
status = "disabled";
};
+/ {
+ sound: sound {
+ /* Compatible is set for each machine */
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ dai-tdm-idle-mode-0 = "zero";
+ dai-tdm-idle-mode-3 = "zero";
+ dai-tdm-slot-tx-idle-mask-0 = <0xf0f0f0>;
+ dai-tdm-slot-tx-idle-mask-3 = <0x0f0f0f>;
+
+ link-name = "Speakers";
+
+ codec {
+ sound-dai = <&speaker_left_woof1>,
+ <&speaker_left_tweet>,
+ <&speaker_left_woof2>,
+ <&speaker_right_woof1>,
+ <&speaker_right_tweet>,
+ <&speaker_right_woof2>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>, <&mca 1>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "spi1-nvram.dtsi"
#include "hwmon-laptop.dtsi"
#include "hwmon-fan-dual.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 19/21] arm64: dts: apple: t600x-j375: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (17 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
2026-10-18 5:54 ` [PATCH v3 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M1 Max/Ultra Mac Studio integrates Apple-specific variants of
the TI TAS2764 and Cirrus CS42L42. Add these nodes and advertise
them to the machine driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../arm64/boot/dts/apple/t6001-j375c.dts | 5 ++
.../arm64/boot/dts/apple/t6002-j375d.dts | 5 ++
.../arm64/boot/dts/apple/t600x-j375.dtsi | 65 +++++++++++++++++++++++++
3 files changed, 75 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t6001-j375c.dts b/arch/arm64/boot/dts/apple/t6001-j375c.dts
index 08276114c1d8..2e544f43b085 100644
--- a/arch/arm64/boot/dts/apple/t6001-j375c.dts
+++ b/arch/arm64/boot/dts/apple/t6001-j375c.dts
@@ -25,4 +25,9 @@ &bluetooth0 {
brcm,board-type = "apple,okinawa";
};
+&sound {
+ compatible = "apple,j375-macaudio";
+ model = "Mac Studio J375";
+};
+
#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t6002-j375d.dts b/arch/arm64/boot/dts/apple/t6002-j375d.dts
index e6eb6b6c386d..dfdf90fb1825 100644
--- a/arch/arm64/boot/dts/apple/t6002-j375d.dts
+++ b/arch/arm64/boot/dts/apple/t6002-j375d.dts
@@ -184,6 +184,11 @@ atcphy5_usb3: endpoint {
};
};
+&sound {
+ compatible = "apple,j375-macaudio";
+ model = "Mac Studio J375";
+};
+
#include "hwmon-fan-dual.dtsi"
/* delete unused USB nodes on die 1 */
diff --git a/arch/arm64/boot/dts/apple/t600x-j375.dtsi b/arch/arm64/boot/dts/apple/t600x-j375.dtsi
index 8a1494949e4c..6c34fc86af74 100644
--- a/arch/arm64/boot/dts/apple/t600x-j375.dtsi
+++ b/arch/arm64/boot/dts/apple/t600x-j375.dtsi
@@ -179,6 +179,34 @@ typec3_connector_ss: endpoint {
};
};
+/* Audio codecs */
+&i2c1 {
+ status = "okay";
+
+ speaker: codec@38 {
+ compatible = "ti,sn012776";
+ reg = <0x38>;
+ interrupts-extended = <&pinctrl_ap 179 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 178 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_HIGH>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Jack";
+ };
+};
+
/* USB controllers */
&dwc3_0 {
ports {
@@ -410,4 +438,41 @@ &pcie0_dart_3 {
status = "okay";
};
+/ {
+ sound: sound {
+ /* Compatible is set for each machine */
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dai-link@0 {
+ reg = <0>;
+
+ link-name = "Speaker";
+
+ codec {
+ sound-dai = <&speaker>;
+ };
+
+ cpu {
+ sound-dai = <&mca 0>;
+ };
+ };
+
+ dai-link@1 {
+ reg = <1>;
+
+ link-name = "Headphone Jack";
+
+ codec {
+ sound-dai = <&jack_codec>;
+ };
+
+ cpu {
+ sound-dai = <&mca 2>;
+ };
+ };
+ };
+};
+
#include "spi1-nvram.dtsi"
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 20/21] arm64: dts: apple: t602x-j41x: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (18 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
2026-10-18 5:54 ` [PATCH v3 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 Pro/Max MacBooks integrate Apple-specific variants of
the TI TAS2764 and Cirrus CS42L42. Add these nodes and advertise
them to the machine driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../arm64/boot/dts/apple/t6020-j414s.dts | 5 ++++
.../arm64/boot/dts/apple/t6020-j416s.dts | 5 ++++
.../arm64/boot/dts/apple/t6021-j414c.dts | 5 ++++
.../arm64/boot/dts/apple/t6021-j416c.dts | 5 ++++
.../boot/dts/apple/t602x-j414-j416.dtsi | 35 +++++++++++++++++++++++++
5 files changed, 55 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t6020-j414s.dts b/arch/arm64/boot/dts/apple/t6020-j414s.dts
index 631c54c5f03d..2df6a39c719d 100644
--- a/arch/arm64/boot/dts/apple/t6020-j414s.dts
+++ b/arch/arm64/boot/dts/apple/t6020-j414s.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,tokara";
};
+
+&sound {
+ compatible = "apple,j414-macaudio", "apple,j314-macaudio";
+ model = "MacBook Pro J414";
+};
diff --git a/arch/arm64/boot/dts/apple/t6020-j416s.dts b/arch/arm64/boot/dts/apple/t6020-j416s.dts
index c277ed5889a2..7f3f0d6eee87 100644
--- a/arch/arm64/boot/dts/apple/t6020-j416s.dts
+++ b/arch/arm64/boot/dts/apple/t6020-j416s.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,amami";
};
+
+&sound {
+ compatible = "apple,j416-macaudio", "apple,j316-macaudio";
+ model = "MacBook Pro J416";
+};
diff --git a/arch/arm64/boot/dts/apple/t6021-j414c.dts b/arch/arm64/boot/dts/apple/t6021-j414c.dts
index cdcf0740714d..ec4050ec0ecf 100644
--- a/arch/arm64/boot/dts/apple/t6021-j414c.dts
+++ b/arch/arm64/boot/dts/apple/t6021-j414c.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,tokara";
};
+
+&sound {
+ compatible = "apple,j414-macaudio", "apple,j314-macaudio";
+ model = "MacBook Pro J414";
+};
diff --git a/arch/arm64/boot/dts/apple/t6021-j416c.dts b/arch/arm64/boot/dts/apple/t6021-j416c.dts
index 6d8146b94170..0037346959a5 100644
--- a/arch/arm64/boot/dts/apple/t6021-j416c.dts
+++ b/arch/arm64/boot/dts/apple/t6021-j416c.dts
@@ -24,3 +24,8 @@ &wifi0 {
&bluetooth0 {
brcm,board-type = "apple,amami";
};
+
+&sound {
+ compatible = "apple,j416-macaudio", "apple,j316-macaudio";
+ model = "MacBook Pro J416";
+};
diff --git a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi
index 0e806d8ddf81..852fbcd6277a 100644
--- a/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi
+++ b/arch/arm64/boot/dts/apple/t602x-j414-j416.dtsi
@@ -43,3 +43,38 @@ &wifi0 {
&bluetooth0 {
compatible = "pci14e4,5f72";
};
+
+&speaker_left_tweet {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&speaker_left_woof1 {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&speaker_left_woof2 {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&speaker_right_tweet {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&speaker_right_woof1 {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&speaker_right_woof2 {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&jack_codec {
+ interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>;
+};
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 21/21] arm64: dts: apple: t602x-j47x: Add speaker/headset jack nodes
2026-10-18 5:53 [PATCH v3 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
` (19 preceding siblings ...)
2026-10-18 5:54 ` [PATCH v3 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
@ 2026-10-18 5:54 ` James Calligeros
20 siblings, 0 replies; 28+ messages in thread
From: James Calligeros @ 2026-10-18 5:54 UTC (permalink / raw)
To: David Rhodes, Richard Fitzgerald, Martin Povišer,
Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, James Calligeros, Jaroslav Kysela, Takashi Iwai,
Matthias Brugger, AngeloGioacchino Del Regno, Shenghao Ding,
Kevin Lu, Baojun Xu, Sen Wang, Sven Peter, Janne Grunau,
Neal Gompa
Cc: asahi, linux-sound, patches, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek
The M2 Pro/Max/Ultra Mac mini and Mac Studio integrate Apple-specific
variants of the TI TAS2764 and Cirrus CS42L42. Add these nodes and
advertise them to the machine driver via the sound node.
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
.../arm64/boot/dts/apple/t6020-j474s.dts | 5 +++++
.../arm64/boot/dts/apple/t6021-j475c.dts | 5 +++++
.../arm64/boot/dts/apple/t6022-j475d.dts | 5 +++++
.../boot/dts/apple/t602x-j474-j475.dtsi | 10 ++++++++++
4 files changed, 25 insertions(+)
diff --git a/arch/arm64/boot/dts/apple/t6020-j474s.dts b/arch/arm64/boot/dts/apple/t6020-j474s.dts
index e3c2635d84fa..f39697490365 100644
--- a/arch/arm64/boot/dts/apple/t6020-j474s.dts
+++ b/arch/arm64/boot/dts/apple/t6020-j474s.dts
@@ -46,4 +46,9 @@ &pcie0 {
<0x300 &pcie0_dart_3 1 1>;
};
+&sound {
+ compatible = "apple,j474-macaudio", "apple,j473-macaudio";
+ model = "Mac mini J474";
+};
+
#include "hwmon-mini.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t6021-j475c.dts b/arch/arm64/boot/dts/apple/t6021-j475c.dts
index 9fe17f1f7f59..f8f776f9eb1e 100644
--- a/arch/arm64/boot/dts/apple/t6021-j475c.dts
+++ b/arch/arm64/boot/dts/apple/t6021-j475c.dts
@@ -36,4 +36,9 @@ &pcie0_dart_1 {
status = "okay";
};
+&sound {
+ compatible = "apple,j475-macaudio", "apple,j375-macaudio";
+ model = "Mac Studio J475";
+};
+
#include "hwmon-fan-dual.dtsi"
diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts
index 32938a3bf96a..153899f1569b 100644
--- a/arch/arm64/boot/dts/apple/t6022-j475d.dts
+++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts
@@ -73,3 +73,8 @@ &bluetooth0 {
compatible = "pci14e4,5f72";
brcm,board-type = "apple,canary";
};
+
+&sound {
+ compatible = "apple,j475-macaudio", "apple,j375-macaudio";
+ model = "Mac Studio J475";
+};
diff --git a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi
index 926bb4ab77af..7f1faefcf9bb 100644
--- a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi
+++ b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi
@@ -36,3 +36,13 @@ &hpm2 {
&hpm3 {
interrupts = <44 IRQ_TYPE_LEVEL_LOW>;
};
+
+&speaker {
+ interrupts-extended = <&pinctrl_ap 58 IRQ_TYPE_LEVEL_LOW>;
+ shutdown-gpios = <&pinctrl_ap 57 GPIO_ACTIVE_HIGH>;
+};
+
+&jack_codec {
+ interrupts-extended = <&pinctrl_ap 59 IRQ_TYPE_LEVEL_LOW>;
+ reset-gpios = <&pinctrl_nub 8 GPIO_ACTIVE_HIGH>;
+};
--
2.56.0
^ permalink raw reply related [flat|nested] 28+ messages in thread