diff --git a/Documentation/devicetree/bindings/soc/apple/apple,t6041-aop.yaml b/Documentation/devicetree/bindings/soc/apple/apple,t6041-aop.yaml index e8a2df805059a..8e940d46e6d67 100644 --- a/Documentation/devicetree/bindings/soc/apple/apple,t6041-aop.yaml +++ b/Documentation/devicetree/bindings/soc/apple/apple,t6041-aop.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/soc/apple/apple,t6041-aop.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Apple T604x Always-On Processor +title: Apple T604x and T8132 Always-On Processor maintainers: - Jonathan Goyvaerts @@ -16,9 +16,15 @@ description: reaches its services through the RTKit endpoints. The firmware's external code and log segments live in DRAM, and the bootloader reserves them. + On T8132 the bootloader also programs the AOP DART's address filter (DAPF) + and reserves its windows, which the firmware reaches through its DART at + their physical addresses. + properties: compatible: - const: apple,t6041-aop + enum: + - apple,t6041-aop + - apple,t8132-aop reg: minItems: 2 @@ -47,11 +53,12 @@ properties: memory-region: minItems: 1 - maxItems: 4 + maxItems: 18 description: - The firmware segments in DRAM (its external code and its log), filled in - by the bootloader from the ADT. The iommu-addresses property of each - region gives the address the firmware uses for it. + The firmware segments in DRAM (its external code and its log) and, on + T8132, the DAPF windows, filled in by the bootloader from the ADT. The + iommu-addresses property of each region gives the address the firmware + uses for it. audio: type: object @@ -93,6 +100,16 @@ required: - mbox-names - iommus +allOf: + - if: + properties: + compatible: + const: apple,t6041-aop + then: + properties: + memory-region: + maxItems: 4 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml index fdf3d1883a99d..fcf97451df97a 100644 --- a/Documentation/devicetree/bindings/sound/apple,macaudio.yaml +++ b/Documentation/devicetree/bindings/sound/apple,macaudio.yaml @@ -23,6 +23,8 @@ properties: - apple,j616-macaudio - apple,j616-macaudio-jack - apple,j700-macaudio + - apple,j713-macaudio + - apple,j713-macaudio-jack - const: apple,macaudio "#address-cells": diff --git a/Documentation/devicetree/bindings/sound/apple,t6041-aop-audio.yaml b/Documentation/devicetree/bindings/sound/apple,t6041-aop-audio.yaml index 1eadf9ff3b6eb..85e9aca39bf7f 100644 --- a/Documentation/devicetree/bindings/sound/apple,t6041-aop-audio.yaml +++ b/Documentation/devicetree/bindings/sound/apple,t6041-aop-audio.yaml @@ -4,21 +4,21 @@ $id: http://devicetree.org/schemas/sound/apple,t6041-aop-audio.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Apple T6041 AOP audio +title: Apple T6041 and T8132 AOP audio maintainers: - Jonathan Goyvaerts description: | - The Always-On Processor on Apple T6041 SoCs runs the machine's audio + The Always-On Processor on Apple T6041 and T8132 SoCs runs the machine's audio streams in its firmware: the microphone array, the internal speakers with their voltage and current sense, and the 3.5 mm headset jack in both directions. This node is a child of the AOP node and names the ADMAC channels the host moves each stream's data over while the firmware runs its serializer. The parent AOP binding describes the firmware transport. - The headset jack's channels are optional: a node without them has no jack - streams. + The speaker and headset jack channels are optional: a node without them has + no speaker or jack streams. # Only select T6041 nodes: other AOP audio nodes carry the # apple,t6030-aop-audio fallback too and have a different set of streams. @@ -35,27 +35,46 @@ allOf: properties: compatible: - items: - - const: apple,t6041-aop-audio - - const: apple,t6030-aop-audio + oneOf: + - items: + - const: apple,t6041-aop-audio + - const: apple,t6030-aop-audio + - items: + - const: apple,t8132-aop-audio + - const: apple,t6041-aop-audio + - const: apple,t6030-aop-audio dmas: - minItems: 3 - items: - - description: Microphone array input - - description: Internal speaker output - - description: Speaker voltage and current sense input - - description: Headset jack output - - description: Headset microphone input + description: + The ADMAC channels named in dma-names, in that order. "dma" is the + microphone array input, "spkr" the internal speaker output, "sense" the + speakers' voltage and current sense input, "cout" the headset jack output + and "cin" the headset microphone input. + minItems: 1 + maxItems: 5 dma-names: - minItems: 3 - items: - - const: dma - - const: spkr - - const: sense - - const: cout - - const: cin + oneOf: + - description: The microphones only + items: + - const: dma + - description: The microphones and the headset jack + items: + - const: dma + - const: cout + - const: cin + - description: The microphones and the speakers + items: + - const: dma + - const: spkr + - const: sense + - description: The microphones, the speakers and the headset jack + items: + - const: dma + - const: spkr + - const: sense + - const: cout + - const: cin "#sound-dai-cells": const: 1 diff --git a/arch/arm64/boot/dts/apple/t8132-j713-pmgr.dtsi b/arch/arm64/boot/dts/apple/t8132-j713-pmgr.dtsi index 5606ce4d2d3e4..1e100e890be0a 100644 --- a/arch/arm64/boot/dts/apple/t8132-j713-pmgr.dtsi +++ b/arch/arm64/boot/dts/apple/t8132-j713-pmgr.dtsi @@ -125,16 +125,18 @@ status = "disabled"; }; +/* The CS42L84 headphone codec. */ &ps_i2c1 { - status = "disabled"; + status = "okay"; }; &ps_i2c2 { status = "disabled"; }; +/* The SN012776 speaker amps. */ &ps_i2c3 { - status = "disabled"; + status = "okay"; }; &ps_i2c4 { diff --git a/arch/arm64/boot/dts/apple/t8132-j713.dts b/arch/arm64/boot/dts/apple/t8132-j713.dts index d878b72fd39ba..d92d0b3b71928 100644 --- a/arch/arm64/boot/dts/apple/t8132-j713.dts +++ b/arch/arm64/boot/dts/apple/t8132-j713.dts @@ -36,6 +36,76 @@ wifi0 = &wifi0; }; + /* + * The speaker amps' shared shutdown line (SDZ, AP GPIO 15, the ADT + * function-reset; low = shutdown). One fixed regulator, the SDZ supply + * of all four, so one amp's fault takes the whole group down. + */ + speaker_sdz: regulator-sn012776-sdz { + compatible = "regulator-fixed"; + regulator-name = "sn012776-sdz"; + gpios = <&pinctrl_ap 15 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + /* + * The AOP firmware runs the stream (MCA cluster 0, one serializer for + * the four amps); macaudio's J713 variant links the AOP audio + * component's speaker back-end (DAI 3) to them. The codecs are listed in + * the ADT provider order, so each gets its native playback slot (LW 0, + * RW 1, LT 2, RT 3). The idle masks are the ADT amp-tx-zd-config rows 0 + * and 1 over the 16 sense byte slots: the left woofer drives zeros in + * the right amps' sense slots, the right woofer in the left amps'. + */ + sound: sound { + compatible = "apple,j713-macaudio", "apple,macaudio"; + model = "MacBook Air J713"; + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Speakers"; + dai-tdm-idle-mode-0 = "zero"; + dai-tdm-idle-mode-1 = "zero"; + dai-tdm-slot-tx-idle-mask-0 = <0xf0f0>; + dai-tdm-slot-tx-idle-mask-1 = <0x0f0f>; + + cpu { + sound-dai = <&aop_audio 3>; + }; + + codec { + sound-dai = <&speaker_left_woof>, <&speaker_right_woof>, + <&speaker_left_tweet>, <&speaker_right_tweet>; + }; + }; + }; + + /* + * The headphone jack: the AOP audio component's jack back-end (DAI 4) + * to the CS42L84, as a card of its own. + */ + sound_jack: sound-jack { + compatible = "apple,j713-macaudio-jack", "apple,macaudio"; + model = "MacBook Air J713 Jack"; + #address-cells = <1>; + #size-cells = <0>; + + dai-link@0 { + reg = <0>; + link-name = "Headphone Jack"; + + cpu { + sound-dai = <&aop_audio 4>; + }; + + codec { + sound-dai = <&jack_codec>; + }; + }; + }; + chosen { linux-enablement-mac,disk-boot; linux-enablement-mac,dapf-handoff = "/arm-io/aop", "/arm-io/mtp", @@ -56,6 +126,122 @@ }; }; +/* + * m1n1 programs the AOP DART's DAPF and reserves the firmware segments and + * the DAPF windows in memory-region when /chosen asks for the AOP's handoff, + * or disables the AOP. + */ +&aop { + status = "okay"; +}; + +&aop_audio { + /* + * The microphones on LEAP; the speakers and their sense on base-ns TX0 + * and RX0 (the J616s stream: six playback and twelve sense slots, of + * which the four amps take the first four and eight), the jack on TX2 + * and RX2. + */ + dmas = <&admac0 1>, <&admac1 0>, <&admac1 1>, <&admac1 4>, <&admac1 5>; + dma-names = "dma", "spkr", "sense", "cout", "cin"; + apple,chassis-name = "J713"; + apple,machine-kind = "MacBook Air"; +}; + +/* + * The four SN012776 speaker amps, held in hardware shutdown until the sound + * card enables them (ADT /arm-io/i2c3/audio-speaker at 0x38 and its + * speaker1..3): one interrupt line on AP GPIO 14, level low; sense slots from + * speaker-config (amp i at RX bytes 4i and 4i + 2, order LW RW LT RT). Names + * as speakersafetyd's j413/j613 profiles use them. Every amp joins the SDZ + * fail-closed group. + */ +&i2c3 { + status = "okay"; + + speaker_left_woof: codec@38 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x38>; + SDZ-supply = <&speaker_sdz>; + apple,sdz-group-fail-closed; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Woofer"; + interrupts-extended = <&pinctrl_ap 14 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <0>; + ti,vmon-slot-no = <2>; + }; + + speaker_left_tweet: codec@39 { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x39>; + SDZ-supply = <&speaker_sdz>; + apple,sdz-group-fail-closed; + #sound-dai-cells = <0>; + sound-name-prefix = "Left Tweeter"; + interrupts-extended = <&pinctrl_ap 14 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <8>; + ti,vmon-slot-no = <10>; + }; + + speaker_right_woof: codec@3b { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3b>; + SDZ-supply = <&speaker_sdz>; + apple,sdz-group-fail-closed; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Woofer"; + interrupts-extended = <&pinctrl_ap 14 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <4>; + ti,vmon-slot-no = <6>; + }; + + speaker_right_tweet: codec@3c { + compatible = "ti,sn012776", "ti,tas2764"; + reg = <0x3c>; + SDZ-supply = <&speaker_sdz>; + apple,sdz-group-fail-closed; + #sound-dai-cells = <0>; + sound-name-prefix = "Right Tweeter"; + interrupts-extended = <&pinctrl_ap 14 IRQ_TYPE_LEVEL_LOW>; + ti,imon-slot-no = <12>; + ti,vmon-slot-no = <14>; + }; +}; + +/* + * The CS42L84 headphone codec (ADT /arm-io/i2c1/audio-codec-output at 0x4b). + * Its reset line is nub GPIO 17, driven high to run the codec; its interrupt + * is AP GPIO 98, level low. The AOP firmware runs the jack's stream. + */ +&i2c1 { + status = "okay"; + + jack_codec: codec@4b { + compatible = "cirrus,cs42l84"; + reg = <0x4b>; + reset-gpios = <&pinctrl_nub 17 GPIO_ACTIVE_HIGH>; + #sound-dai-cells = <0>; + interrupts-extended = <&pinctrl_ap 98 IRQ_TYPE_LEVEL_LOW>; + sound-name-prefix = "Jack"; + }; +}; + +&aop_dart { + status = "okay"; +}; + +&aop_mbox { + status = "okay"; +}; + +&admac0 { + status = "okay"; +}; + +&admac1 { + status = "okay"; +}; + /* Enable DVFS only with the J713 board thermal owner. */ &cpufreq_e { status = "okay"; diff --git a/arch/arm64/boot/dts/apple/t8132.dtsi b/arch/arm64/boot/dts/apple/t8132.dtsi index c82816625efd0..77dccc298914d 100644 --- a/arch/arm64/boot/dts/apple/t8132.dtsi +++ b/arch/arm64/boot/dts/apple/t8132.dtsi @@ -825,6 +825,84 @@ /* Shared controller resources; board files select the qualified devices. */ &soc { + /* + * AOP (always-on processor). iBoot loads its RTKit firmware and leaves + * it halted; Linux starts it. + */ + aop: aop@390600000 { + compatible = "apple,t8132-aop"; + reg = <0x3 0x90c00000 0x0 0x1e0000>, + <0x3 0x90600000 0x0 0x88000>, + <0x3 0x90050000 0x0 0x4000>, + <0x3 0x882a8000 0x0 0x8>; + reg-names = "firmware", "asc", "asc-aux", "config"; + mboxes = <&aop_mbox>; + mbox-names = "mbox"; + iommus = <&aop_dart 0>; + status = "disabled"; + + /* + * "dma" is the microphones' LEAP ADMAC channel (ADT audio-hp); + * the speaker and jack channels follow the T6041 map. + */ + aop_audio: audio { + compatible = "apple,t8132-aop-audio", "apple,t6041-aop-audio", + "apple,t6030-aop-audio"; + dmas = <&admac0 1>; + dma-names = "dma"; + #sound-dai-cells = <1>; + }; + }; + + /* ADT lists the interrupts as 1, 0, 3, 2 of the block: send-empty is the lowest. */ + aop_mbox: mailbox@390608000 { + compatible = "apple,t8132-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x90608000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + status = "disabled"; + }; + + aop_dart: iommu@390f00000 { + compatible = "apple,t8132-dart", "apple,t8110-dart"; + reg = <0x3 0x90f00000 0x0 0xc000>; + #iommu-cells = <1>; + interrupt-parent = <&aic>; + interrupts = ; + status = "disabled"; + }; + + /* The audio fabric's ADMACs: admac-leap-ns (DART stream 10) and admac-base-ns (stream 8) */ + admac0: dma-controller@393300000 { + compatible = "apple,t8132-admac"; + reg = <0x3 0x93300000 0x0 0x34000>; + #dma-cells = <1>; + dma-channels = <9>; + /* the ADT gives the first of the four outputs */ + interrupts-extended = <&aic AIC_IRQ 478 IRQ_TYPE_LEVEL_HIGH>, + <0>, <0>, <0>; + iommus = <&aop_dart 10>; + status = "disabled"; + }; + + admac1: dma-controller@393800000 { + compatible = "apple,t8132-admac"; + reg = <0x3 0x93800000 0x0 0x34000>; + #dma-cells = <1>; + dma-channels = <13>; + /* the ADT gives the first of the four outputs */ + interrupts-extended = <&aic AIC_IRQ 475 IRQ_TYPE_LEVEL_HIGH>, + <0>, <0>, <0>; + iommus = <&aop_dart 8>; + status = "disabled"; + }; + nub_spmi_a0: spmi@388908000 { status = "disabled"; compatible = "apple,t8132-spmi", "apple,t8103-spmi"; diff --git a/drivers/soc/apple/aop.rs b/drivers/soc/apple/aop.rs index e72f025bb3459..e220fffc195db 100644 --- a/drivers/soc/apple/aop.rs +++ b/drivers/soc/apple/aop.rs @@ -2519,6 +2519,10 @@ const HW_CFG_T6040: AopHwConfig = AopHwConfig { fw_regions: true, halt_on_failed_start: true, }; +/// T8132 (J713): as T6040. The bootloader hands the AOP over the same way, with +/// the DART's DAPF programmed and its windows reserved alongside the firmware +/// segments. +const HW_CFG_T8132: AopHwConfig = HW_CFG_T6040; /// T8140: the firmware is started with the boot arguments the bootloader /// left, boots through the setup port and speaks EPIC version 4. Of the /// advertised AFK endpoints, the application map is 0x20 misc, 0x21 @@ -2547,6 +2551,7 @@ kernel::of_device_table!( (of::DeviceId::new(c_str!("apple,t6020-aop")), &HW_CFG_T6020), (of::DeviceId::new(c_str!("apple,t8140-aop")), &HW_CFG_T8140), (of::DeviceId::new(c_str!("apple,t6041-aop")), &HW_CFG_T6040), + (of::DeviceId::new(c_str!("apple,t8132-aop")), &HW_CFG_T8132), ] ); diff --git a/sound/soc/apple/aop_audio.rs b/sound/soc/apple/aop_audio.rs index 5431943d46c88..f148f7a25492c 100644 --- a/sound/soc/apple/aop_audio.rs +++ b/sound/soc/apple/aop_audio.rs @@ -1092,12 +1092,14 @@ impl platform::Driver for SndSocAopDriver { dev_warn!(data.dev, "decimator config refused, continuing\n"); } let mut this = Self::new(data.clone())?; - // The speaker stream, where the node has its DMA channel. - if data - .fwnode - .property_match_string(c_str!("dma-names"), c_str!("spkr")) - .is_ok() - { + // The speaker and jack streams, where the node has their DMA + // channels: a machine may have the jack before its speakers. + let listed = |name: &CStr| { + data.fwnode + .property_match_string(c_str!("dma-names"), name) + .is_ok() + }; + if listed(c_str!("spkr")) || (listed(c_str!("cout")) && listed(c_str!("cin"))) { this.spkr = Some(aop_spkr::SpeakerComponent::register(data)?); } return Ok(this); diff --git a/sound/soc/apple/aop_spkr.rs b/sound/soc/apple/aop_spkr.rs index 0f0de5b0afaa7..1823197b4404d 100644 --- a/sound/soc/apple/aop_spkr.rs +++ b/sound/soc/apple/aop_spkr.rs @@ -1604,9 +1604,9 @@ unsafe extern "C" fn jack_resume(component: *mut bindings::snd_soc_component) -> } } -/// The speaker component, and the jack's where the audio node lists its DMA channels. +/// The speaker and jack components, each where the audio node lists its DMA channels. pub(crate) struct SpeakerComponent { - _spkr: Registration, + _spkr: Option, _jack: Option, } @@ -1641,19 +1641,29 @@ impl SpeakerComponent { } else { None }; - let mut spkr_driver = component_driver(c_str!("t6040-aop-spkr")); - spkr_driver.suspend = Some(spkr_suspend); - spkr_driver.resume = Some(spkr_resume); - let spkr = Registration::new(&aop, services, spkr_driver, |ops| { - to_kvec(speaker_dais(ops)) - }) - .inspect_err(|e| { - dev_err!( - aop.dev, - "Unable to register the speaker ASoC component: {}\n", - e.to_errno() + let has_spkr = aop + .fwnode + .property_match_string(c_str!("dma-names"), c_str!("spkr")) + .is_ok(); + let spkr = if has_spkr { + let mut spkr_driver = component_driver(c_str!("t6040-aop-spkr")); + spkr_driver.suspend = Some(spkr_suspend); + spkr_driver.resume = Some(spkr_resume); + Some( + Registration::new(&aop, services, spkr_driver, |ops| { + to_kvec(speaker_dais(ops)) + }) + .inspect_err(|e| { + dev_err!( + aop.dev, + "Unable to register the speaker ASoC component: {}\n", + e.to_errno() + ) + })?, ) - })?; + } else { + None + }; Ok(Self { _spkr: spkr, _jack: jack, diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c index cc1123e10f398..903e8dbd13059 100644 --- a/sound/soc/apple/macaudio.c +++ b/sound/soc/apple/macaudio.c @@ -123,6 +123,12 @@ struct macaudio_platform_cfg { * (CS42L84_DAC_DB()), 0 for the codec's own maximum. */ int jack_max_vol; + /* + * The speaker front-end's channel count where the CPU component's + * stream has more slots than amps (0: one channel per amp). The amps + * keep the first slots in codec order; the others carry silence. + */ + unsigned int speaker_fe_channels; }; static const char *volume_control_names[] = { @@ -938,6 +944,8 @@ static int macaudio_parse_of(struct macaudio_snd_data *ma) /* Save the max number of channels on the platform */ if (nchannels > ma->max_channels) ma->max_channels = nchannels; + if (speakers && ma->cfg->speaker_fe_channels > ma->max_channels) + ma->max_channels = ma->cfg->speaker_fe_channels; /* * If there is a single speaker, assign two channels to it, because @@ -2041,6 +2049,28 @@ struct macaudio_platform_cfg macaudio_j616_cfg = { .fe_cpu_is_platform = true, }; +/* + * J713 (T8132, MacBook Air 13" M4): the J616s stream, six 32-bit playback + * and twelve 16-bit sense slots in the same 250-bit frame, with four + * SN012776 amps, a woofer and a tweeter per side, on the first four and the + * first eight. The front-end carries all six channels; the last two are + * silent. + */ +struct macaudio_platform_cfg macaudio_j713_cfg = { + .enable_speakers = true, + .amp = AMP_SN012776, + .speakers = SPKR_1W1T, + .stereo = true, + .amp_gain = 15, + .safe_vol = -20, + .fe_dai_names = { "j616-pcm-0", "j616-pcm-1", "j616-pcm-2" }, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_GATED | SND_SOC_DAIFMT_NB_IF, + .secondary_bclk_ratio = 250, + .fe_cpu_is_platform = true, + .speaker_fe_channels = 6, +}; + /* * The J616s headphone jack, a card of its own: a CS42L84 on the AOP's * "jack" back-end, which the firmware runs on MCA cluster 2 as a 64-SCLK @@ -2129,6 +2159,10 @@ static const struct of_device_id macaudio_snd_device_id[] = { { .compatible = "apple,j616-macaudio-jack", .data = &macaudio_j616_jack_cfg }, /* j700 AID44 max98360a - 2x 1W (AOP-fed, software gain) */ { .compatible = "apple,j700-macaudio", .data = &macaudio_j700_cfg }, + /* j713 - sn012776 15 2x 1W+1T (AOP-fed, one serializer) */ + { .compatible = "apple,j713-macaudio", .data = &macaudio_j713_cfg }, + /* The J713 jack runs on the AOP like the J616s one, with the same cap. */ + { .compatible = "apple,j713-macaudio-jack", .data = &macaudio_j616_jack_cfg }, /* Fallback, jack only */ { .compatible = "apple,macaudio"}, { }