mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs
@ 2026-10-04  6:03 James Calligeros
  2026-10-04  6:03 ` [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
                   ` (20 more replies)
  0 siblings, 21 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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

Hi folks,

This series adds the machine driver and related bindings for the
primary audio subsystem on Apple Silicon Macs. The macaudio
machine driver is based on an RFC sent many years ago
by Martin Povišer[1]. 

The driver uses the DPCM model to dynamically
glue a backend DAI (combination of MCA ports and codecs) to a
frontend PCM. While this theoretically allows us to route any
backend to any frontend, this is not used in practice; the "Primary"
frontend is always used for the headset codec and the "Secondary"
is always used for the speakers.

The audio subsystem on Apple Silicon Macs is rather complicated and
the speaker arrangement is highly variable; some machines have a
single mono woofer while others pull together multiple woofers
and tweeters into a single stereo pair. Every machine also includes
a headset jack, and some also have their own builtin microphones which
are handled by a separate coprocessor with its own driver.

Many functions which are handled by firmware on other platforms
must be handled by the kernel or by userspace. Most notably, speaker
protection is handled by speakersafetyd[2], a userspace daemon.
Speaker protection is handled in userspace due to the expensive
floating point calculations and amount of per-machine configuration
data required.

The machine driver facilitates this by exposing a capture
PCM from which the voltage and current across each speaker's voice
coil can be read. Given that there are absolutely no hardware or
firmware level failsafes preventing permanent damage to the fragile
speakers found on these machines, the driver also implements a
failsafe that will significantly attenuate and lock the gains
of every connected speaker unless speakersafetyd is running.

Version 2 of this series drops numerous fixes and semi-related
patches that are not strictly necessary for the machine driver
to function correctly. These will be resubmitted at a later date.

Adding the required Devicetree updates for M3 series devices would
have bloated this series to an unreasonable size as they are still
missing most prerequisite hardware nodes. They will be added in a
future series.

Regards,
James

[1] https://lore.kernel.org/asahi/20220606191910.16580-1-povik+lin@cutebit.org/
[2] https://github.com/AsahiLinux/speakersafetyd

---
Changes in v2:
- Drop patches that are only tangentially related (will be submitted
  later)
- Drop wildcard-matching API for kctls
- Relabel J473 sound model to Mac mini
- Align Devicetree changes with upstream bindings
- Relax Cirrus CS42L42 required properties to keep DT style checker happy
- Define missing properties in CS42L{42,84} bindings
- Fix mistyped DT properties
- Improve macaudio DT binding description
- Drop generic fallback compatible for apple,macaudio
- Drop now-redundant descriptions from other compatibles
- Constrain DAI link link-name property with an enum of valid values
- Drop MCA node from macaudio binding example
- Fix heading comment style in macaudio
- Drop obsolete module parameter
- Replace string functions with safer variants
- Replace dropped wildcard API with searching for full kctl name
- Rework idle props parsing to better surface errors/invalid Devicetree entries
- Add additional locking to backend setup
- Take controls_rwsem when safety functions are called from workqueues
- Replace all mutex{,un}lock calls with guard/scoped_guard
- Fix incorrect amp gain value for j37x config struct
- static const-ify all config structs
- Cancel control lock update workqueue on teardown
- Drop unused DAPM widget
- Fix commented-out safe volume calculations
- Drop speaker lock kcontrol ownership checks and related core API extension
- Link to v1: https://patch.msgid.link/20260920-macaudio-v1-0-741cc20a74e5@gmail.com

To: David Rhodes <david.rhodes@cirrus.com>
To: Richard Fitzgerald <rf@opensource.cirrus.com>
To: Martin Povišer <povik+lin@cutebit.org>
To: Liam Girdwood <lgirdwood@gmail.com>
To: Mark Brown <broonie@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: James Calligeros <jcalligeros99@gmail.com>
To: Jaroslav Kysela <perex@perex.cz>
To: Takashi Iwai <tiwai@suse.com>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
To: Shenghao Ding <shenghao-ding@ti.com>
To: Kevin Lu <kevin-lu@ti.com>
To: Baojun Xu <baojun.xu@ti.com>
To: Sen Wang <sen@ti.com>
To: Sven Peter <sven@kernel.org>
To: Janne Grunau <j@jannau.net>
To: Neal Gompa <neal@gompa.dev>
Cc: asahi@lists.linux.dev
Cc: linux-sound@vger.kernel.org
Cc: patches@opensource.cirrus.com
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org

---
James Calligeros (16):
      ASoC: dt-bindings: cs42l84: Document sound-name-prefix property
      ASoC: dt-bindings: cs42l42: Do not require chip power supplies
      ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property
      ASoC: dt-bindings: cs42l42: Document sound-name-prefix property
      arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes
      arm64: dts: apple: t8103-j313: Add speaker/headset jack nodes
      arm64: dts: apple: t8103-j293: Add speaker/headset jack nodes
      arm64: dts: apple: t8103-j45x: Add headset jack nodes
      arm64: dts: apple: t8112-j413: Add speaker/headset jack nodes
      arm64: dts: apple: t8112-j415: Add speaker/headset jack nodes
      arm64: dts: apple: t8112-j473: Add speaker/headset jack nodes
      arm64: dts: apple: t8112-j493: Add speaker/headset jack nodes
      arm64: dts: apple: t600x-j31x: Add speaker/headset jack nodes
      arm64: dts: apple: t600x-j375: Add speaker/headset jack nodes
      arm64: dts: apple: t602x-j41x: Add speaker/headset jack nodes
      arm64: dts: apple: t602x-j47x: Add speaker/headset jack nodes

Martin Povišer (5):
      ASoC: dt-bindings: Add binding for Apple Silicon Mac audio
      ASoC: ops: Introduce 'snd_soc_deactivate_kctl'
      ASoC: ops: Introduce 'soc_set_enum_kctl'
      ASoC: card: Let 'fixup_controls' return errors
      ASoC: apple: Add macaudio machine driver

 .../bindings/sound/apple,macaudio.yaml   |  210 +++
 .../bindings/sound/cirrus,cs42l42.yaml   |   11 +-
 .../bindings/sound/cirrus,cs42l84.yaml   |    3 +
 .../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 +
 .../arm64/boot/dts/apple/t6001-j375c.dts |    5 +
 .../arm64/boot/dts/apple/t6002-j375d.dts |    5 +
 .../boot/dts/apple/t600x-j314-j316.dtsi  |  135 ++
 .../arm64/boot/dts/apple/t600x-j375.dtsi |   65 +
 .../arm64/boot/dts/apple/t6020-j414s.dts |    5 +
 .../arm64/boot/dts/apple/t6020-j416s.dts |    5 +
 .../arm64/boot/dts/apple/t6020-j474s.dts |    5 +
 .../arm64/boot/dts/apple/t6021-j414c.dts |    5 +
 .../arm64/boot/dts/apple/t6021-j416c.dts |    5 +
 .../arm64/boot/dts/apple/t6021-j475c.dts |    5 +
 .../arm64/boot/dts/apple/t6022-j475d.dts |    5 +
 .../boot/dts/apple/t602x-j414-j416.dtsi  |   35 +
 .../boot/dts/apple/t602x-j474-j475.dtsi  |   10 +
 arch/arm64/boot/dts/apple/t8103-j274.dts |   67 +
 arch/arm64/boot/dts/apple/t8103-j293.dts |  111 ++
 arch/arm64/boot/dts/apple/t8103-j313.dts |   82 ++
 arch/arm64/boot/dts/apple/t8103-j456.dts |   38 +
 arch/arm64/boot/dts/apple/t8103-j457.dts |   38 +
 arch/arm64/boot/dts/apple/t8112-j413.dts |  108 ++
 arch/arm64/boot/dts/apple/t8112-j415.dts |  132 ++
 arch/arm64/boot/dts/apple/t8112-j473.dts |   62 +
 arch/arm64/boot/dts/apple/t8112-j493.dts |  108 ++
 include/sound/soc-card.h                 |    2 +-
 include/sound/soc.h                      |    6 +-
 sound/soc/apple/Kconfig                  |   18 +
 sound/soc/apple/Makefile                 |    4 +
 sound/soc/apple/macaudio.c               | 1699 +++++++++++++++++++++++++
 .../soc/mediatek/mt8188/mt8188-mt6359.c  |    4 +-
 sound/soc/soc-card.c                     |   12 +-
 sound/soc/soc-core.c                     |    5 +-
 sound/soc/soc-ops.c                      |  103 ++
 38 files changed, 3121 insertions(+), 12 deletions(-)
---
base-commit: 74612d0b448fe745e80cda87fe49ac3c102ccc1f
change-id: 20260915-macaudio-33c1c4f304da

Best regards,
--  
James Calligeros <jcalligeros99@gmail.com>


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 02/21] ASoC: dt-bindings: cs42l42: Do not require chip power supplies James Calligeros
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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   | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
index 94062c1d9d45..2637aa5f4326 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
@@ -32,6 +32,9 @@ properties:
   '#sound-dai-cells':
     const: 0
 
+  sound-name-prefix:
+    $ref: component-common.yaml#/properties/sound-name-prefix
+
 required:
   - compatible
   - reg

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 02/21] ASoC: dt-bindings: cs42l42: Do not require chip power supplies
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
  2026-10-04  6:03 ` [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 03/21] ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property James Calligeros
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 integrators do not expose the power rails connected to the codec
in a way that can be expressed in the Devicetree. Do not require them
to be specified on such platforms.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../bindings/sound/cirrus,cs42l42.yaml   | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
index ab4c7d22dc0e..6c100cb36699 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
@@ -187,11 +187,6 @@ properties:
 required:
   - compatible
   - reg
-  - VP-supply
-  - VCP-supply
-  - VD_FILT-supply
-  - VL-supply
-  - VA-supply
 
 additionalProperties: false
 

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 03/21] ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
  2026-10-04  6:03 ` [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
  2026-10-04  6:03 ` [PATCH v2 02/21] ASoC: dt-bindings: cs42l42: Do not require chip power supplies James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 04/21] ASoC: dt-bindings: cs42l42: Document sound-name-prefix property James Calligeros
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 #sound-dai-cells on some machines where this
codec is part of a simple DAI. Document this property to keep the style
checker happy.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 .../bindings/sound/cirrus,cs42l42.yaml   | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
index 6c100cb36699..bc759eaadeca 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
@@ -26,6 +26,9 @@ properties:
       The I2C address of the CS42L42.
     maxItems: 1
 
+  '#sound-dai-cells':
+    const: 0
+
   VP-supply:
     description:
       VP power supply.

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 04/21] ASoC: dt-bindings: cs42l42: Document sound-name-prefix property
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (2 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 03/21] ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 05/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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,cs42l42.yaml   | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
index bc759eaadeca..035463be1456 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
@@ -29,6 +29,9 @@ properties:
   '#sound-dai-cells':
     const: 0
 
+  sound-name-prefix:
+    $ref: component-common.yaml#/properties/sound-name-prefix
+
   VP-supply:
     description:
       VP power supply.

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 05/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (3 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 04/21] ASoC: dt-bindings: cs42l42: Document sound-name-prefix property James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 06/21] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' James Calligeros
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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   | 210 +++++++++++++++++++++++++
 1 file changed, 210 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..c0a04fab82d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml
@@ -0,0 +1,210 @@
+# 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,j473-macaudio
+              - apple,j456-macaudio
+              - apple,j457-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,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:
+  - 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 06/21] ASoC: ops: Introduce 'snd_soc_deactivate_kctl'
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (4 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 05/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 can be used to deactivate controls. 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 | 34 +++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 553bb7e2be98..973ffaf6f060 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_deactivate_kctl(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..4d799c5e44f1 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -471,6 +471,40 @@ int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max)
 }
 EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
 
+/**
+ * snd_soc_deactivate_kctl - Activate/deactivate control matching a name
+ *
+ * @card: where to look for the controls
+ * @name: name
+ * @active: non-zero to activate, zero to deactivate
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_deactivate_kctl(struct snd_soc_card *card,
+	const char *name, int active)
+{
+	struct snd_kcontrol *kctl;
+	int ret = -EINVAL;
+
+	/* Sanity check for name */
+	if (unlikely(!name))
+		return -EINVAL;
+
+	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_deactivate_kctl);
+
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_info *uinfo)
 {

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl'
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (5 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 06/21] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 08/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 69 +++++++++++++++++++++++++
 2 files changed, 71 insertions(+)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 973ffaf6f060..f356b553227d 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_deactivate_kctl(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 4d799c5e44f1..b1eb74e24193 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -505,6 +505,75 @@ int snd_soc_deactivate_kctl(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_deactivate_kctl);
 
+static int soc_set_enum_kctl(struct snd_kcontrol *kctl, const char *value)
+{
+	struct snd_ctl_elem_value evalue = { 0 };
+	struct snd_ctl_elem_info info = { 0 };
+	int sel, i, ret;
+
+	ret = kctl->info(kctl, &info);
+	if (ret < 0)
+		return ret;
+
+	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 -EINVAL;
+
+	for (i = 0; i < info.count; i++)
+		evalue.value.enumerated.item[i] = sel;
+
+	if (kctl->put)
+		return kctl->put(kctl, &evalue);
+	else
+		return -EINVAL;
+}
+
+/**
+ * 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;
+	int ret = -EINVAL;
+
+	/* Sanity check for name */
+	if (unlikely(!name))
+		return -EINVAL;
+
+	kctl = snd_soc_card_get_kcontrol(card, name);
+
+	if (kctl) {
+		ret = soc_set_enum_kctl(kctl, value);
+		if (ret)
+			return ret;
+	} else {
+		dev_dbg(card->dev, "Tried to set unknown control '%s' to '%s'\n",
+			name, value);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_enum_kctl);
+
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_info *uinfo)
 {

-- 
2.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 08/21] ASoC: card: Let 'fixup_controls' return errors
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (6 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver James Calligeros
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 f356b553227d..d638fc69925c 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (7 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 08/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 1699 +++++++++++++++++++++++++
 3 files changed, 1721 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..36a62d88bb18
--- /dev/null
+++ b/sound/soc/apple/macaudio.c
@@ -0,0 +1,1699 @@
+// 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;
+};
+
+struct macaudio_snd_data {
+	struct snd_soc_card card;
+	struct snd_soc_jack jack;
+	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,
+		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),
+		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 *name = volume_control_names[ma->cfg->amp];
+	int namelen = strlen(name);
+
+	if (!name) {
+		WARN_ON_ONCE(1);
+		return;
+	}
+
+	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 = kctl->id.name + strlen(kctl->id.name) - namelen;
+
+		if (strncmp(suffix, name, namelen))
+			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" : "", 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);
+
+	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 macaudio_snd_data *ma,
+				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 = ma->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(ma->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(ma->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 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 snd_soc_card *card = &ma->card;
+	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(ma->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(ma->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(ma, link, be_index,
+							    ncodecs_per_cpu, cpu, codec);
+			if (ret)
+				goto err_free;
+
+			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(ma->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(ma->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];
+
+	guard(mutex)(&ma->be_link_mutex);
+
+	if (props->is_speakers && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		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(ma->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, &ma->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)
+{
+	struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
+	int ret;
+
+	ret = snd_soc_card_jack_new_pins(card, "Headphone Jack",
+			SND_JACK_HEADSET | SND_JACK_HEADPHONE,
+			&ma->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;
+}
+
+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;
+}
+
+#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_deactivate_kctl, "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_deactivate_kctl, "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_deactivate_kctl, "HPF Corner Frequency", 0);
+
+		CHECK_CONCAT(snd_soc_set_enum_kctl, "OCE Handling", "Retry");
+		CHECK_CONCAT(snd_soc_deactivate_kctl, "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_deactivate_kctl, "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(ma->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" },
+};
+
+/*	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 = &data->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->owner = THIS_MODULE;
+	card->driver_name = "macaudio";
+	card->dev = dev;
+	card->dapm_widgets = macaudio_snd_widgets;
+	card->num_dapm_widgets = ARRAY_SIZE(macaudio_snd_widgets);
+	card->dapm_routes = macaudio_dapm_routes;
+	card->num_dapm_routes = ARRAY_SIZE(macaudio_dapm_routes);
+	card->controls = macaudio_controls;
+	card->num_controls = ARRAY_SIZE(macaudio_controls);
+	card->probe = macaudio_probe;
+	card->late_probe = macaudio_late_probe;
+	card->component_chaining = true;
+	card->fully_routed = true;
+
+	if (of_id->data)
+		data->cfg = of_id->data;
+	else
+		data->cfg = &macaudio_fallback_cfg;
+
+	card->fixup_controls = macaudio_fixup_controls;
+
+	ret = macaudio_parse_of(data);
+	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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (8 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 67 +++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/t8103-j274.dts b/arch/arm64/boot/dts/apple/t8103-j274.dts
index 1212852251e0..6121e3a89a0f 100644
--- a/arch/arm64/boot/dts/apple/t8103-j274.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j274.dts
@@ -75,4 +75,71 @@ &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>;
+	};
+};
+
+/ {
+	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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 11/21] arm64: dts: apple: t8103-j313: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (9 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 82 +++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/t8103-j313.dts b/arch/arm64/boot/dts/apple/t8103-j313.dts
index f8b2b1637b9d..cdda63ca8b34 100644
--- a/arch/arm64/boot/dts/apple/t8103-j313.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j313.dts
@@ -55,4 +55,86 @@ &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>;
+	};
+};
+
+/ {
+	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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 12/21] arm64: dts: apple: t8103-j293: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (10 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 111 +++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/t8103-j293.dts b/arch/arm64/boot/dts/apple/t8103-j293.dts
index 1d074b9e6018..42f1127151fa 100644
--- a/arch/arm64/boot/dts/apple/t8103-j293.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j293.dts
@@ -133,5 +133,116 @@ &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>;
+	};
+};
+
+&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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 13/21] arm64: dts: apple: t8103-j45x: Add headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (11 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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 | 38 +++++++++++++++++++++++++
 arch/arm64/boot/dts/apple/t8103-j457.dts | 38 +++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/arch/arm64/boot/dts/apple/t8103-j456.dts b/arch/arm64/boot/dts/apple/t8103-j456.dts
index 090c97bb781b..1fc74ebf9ab2 100644
--- a/arch/arm64/boot/dts/apple/t8103-j456.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j456.dts
@@ -88,3 +88,41 @@ &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>;
+	};
+};
+
+/ {
+	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..d8ea6bb53b3f 100644
--- a/arch/arm64/boot/dts/apple/t8103-j457.dts
+++ b/arch/arm64/boot/dts/apple/t8103-j457.dts
@@ -69,3 +69,41 @@ 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>;
+	};
+};
+
+/ {
+	sound: sound {
+		compatible = "apple,j457-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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (12 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 15/21] arm64: dts: apple: t8112-j415: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (13 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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..4f3db2a93db0 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 Tweet";
+		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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 16/21] arm64: dts: apple: t8112-j473: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (14 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 17/21] arm64: dts: apple: t8112-j493: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (15 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 18/21] arm64: dts: apple: t600x-j31x: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (16 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 19/21] arm64: dts: apple: t600x-j375: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (17 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
  2026-10-04  6:03 ` [PATCH v2 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 20/21] arm64: dts: apple: t602x-j41x: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (18 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  2026-10-04  6:03 ` [PATCH v2 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 21/21] arm64: dts: apple: t602x-j47x: Add speaker/headset jack nodes
  2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
                   ` (19 preceding siblings ...)
  2026-10-04  6:03 ` [PATCH v2 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
@ 2026-10-04  6:03 ` James Calligeros
  20 siblings, 0 replies; 22+ messages in thread
From: James Calligeros @ 2026-10-04  6:03 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..7b4c4ef471be 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 J473";
+};
+
 #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.55.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2026-10-04  6:07 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
2026-10-04  6:03 ` [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
2026-10-04  6:03 ` [PATCH v2 02/21] ASoC: dt-bindings: cs42l42: Do not require chip power supplies James Calligeros
2026-10-04  6:03 ` [PATCH v2 03/21] ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property James Calligeros
2026-10-04  6:03 ` [PATCH v2 04/21] ASoC: dt-bindings: cs42l42: Document sound-name-prefix property James Calligeros
2026-10-04  6:03 ` [PATCH v2 05/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
2026-10-04  6:03 ` [PATCH v2 06/21] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' James Calligeros
2026-10-04  6:03 ` [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
2026-10-04  6:03 ` [PATCH v2 08/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
2026-10-04  6:03 ` [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver James Calligeros
2026-10-04  6:03 ` [PATCH v2 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
2026-10-04  6:03 ` [PATCH v2 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
2026-10-04  6:03 ` [PATCH v2 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
2026-10-04  6:03 ` [PATCH v2 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®